Caldera experiments can be independently controlled by files. Those will overwrite the caldera attacks in the experiment files. Good for batch processing

pull/3/head
Thorsten Sick 3 years ago
parent 12c92939c0
commit 090cd7c16a

@ -198,6 +198,20 @@ class CalderaControl():
res.append(ability)
return res
def does_ability_support_platform(self, abid: str, platform: str) -> bool:
""" Checks if an ability supports a specific os
@param abid: ability id.
@param platform: os string to match for
"""
# caldera knows the os-es "windows", "linux" and "darwin"
for ability in self.get_ability(abid):
if ability["platform"] == platform:
return True
return False
def get_operation_by_id(self, op_id):
""" Get operation by id
@ -492,13 +506,16 @@ class CalderaControl():
# ######## All inclusive methods
def attack(self, attack_logger: AttackLog = None, paw="kickme", ability_id="bd527b63-9f9e-46e0-9816-b8434d2b8989", group="red"):
def attack(self, attack_logger: AttackLog = None, paw="kickme", ability_id="bd527b63-9f9e-46e0-9816-b8434d2b8989", group="red", target_platform=None):
""" Attacks a system and returns results
@param attack_logger: An attack logger class to log attacks with
@param paw: Paw to attack
@param group: Group to attack. Paw must be in the group
@param ability_id: Ability to run against the target
@param target_platform: Platform of the target machine. Optional. Used for quick-outs
@:return : True if the attack was executed. False if it was not. For example the target os is not supported by this attack
"""
# Tested obfuscators (with sandcat):
@ -514,6 +531,14 @@ class CalderaControl():
adversary_name = "generated_adv__" + str(time.time())
operation_name = "testoperation__" + str(time.time())
if target_platform:
# Check if an ability does support the platform of the target:
if not self.does_ability_support_platform(ability_id, target_platform):
self.attack_logger.vprint(
f"{CommandlineColors.FAIL}Platform {target_platform} not supported by {ability_id}{CommandlineColors.ENDC}",
1)
return False
self.add_adversary(adversary_name, ability_id)
adid = self.get_adversary(adversary_name)["adversary_id"]
@ -595,6 +620,7 @@ class CalderaControl():
obfuscator=obfuscator,
jitter=jitter
)
return True
def pretty_print_ability(self, abi):
""" Pretty pritns an ability
@ -603,7 +629,7 @@ class CalderaControl():
"""
print("""
ID: {technique_id}
TTP: {technique_id}
Technique name: {technique_name}
Tactic: {tactic}
Name: {name}

@ -22,11 +22,12 @@ from app.exceptions import ServerError
class Experiment():
""" Class handling experiments """
def __init__(self, configfile, verbosity=0):
def __init__(self, configfile, verbosity=0, caldera_attacks: list = None):
"""
@param configfile: Path to the configfile to load
@param verbosity: verbosity level between 0 and 3
@param caldera_attacks: an optional argument to override caldera attacks in the config file and run just this one caldera attack. A list of caldera ID
"""
self.attacker_1 = None
@ -103,8 +104,9 @@ class Experiment():
# Attack them
self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Running Caldera attacks{CommandlineColors.ENDC}", 1)
for target_1 in self.targets:
# Run caldera attacks
caldera_attacks = self.experiment_config.get_caldera_attacks(target_1.get_os())
if caldera_attacks is None:
# Run caldera attacks
caldera_attacks = self.experiment_config.get_caldera_attacks(target_1.get_os())
if caldera_attacks:
for attack in caldera_attacks:
# TODO: Work with snapshots
@ -112,11 +114,12 @@ class Experiment():
self.attack_logger.vprint(f"Attacking machine with PAW: {target_1.get_paw()} with {attack}", 2)
caldera_control = CalderaControl("http://" + self.attacker_1.getip() + ":8888", self.attack_logger, config=self.experiment_config)
caldera_control.attack(attack_logger=self.attack_logger,
paw=target_1.get_paw(),
ability_id=attack,
group=target_1.get_group(),
)
it_worked = caldera_control.attack(attack_logger=self.attack_logger,
paw=target_1.get_paw(),
ability_id=attack,
group=target_1.get_group(),
target_platform=target_1.get_os()
)
# Moved to fix section below. If fix works: can be removed
# print(f"Pausing before next attack (config: nap_time): {self.experiment_config.get_nap_time()}")
@ -124,23 +127,24 @@ class Experiment():
# Fix: Caldera sometimes gets stuck. This is why we better re-start the caldera server and wait till all the implants re-connected
# Reason: In some scenarios we keep the infra up for hours or days. No re-creation like intended. This can cause Caldera to hick up
self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Restarting caldera server and waiting for clients to re-connect{CommandlineColors.ENDC}", 1)
self.attacker_1.start_caldera_server()
self.attack_logger.vprint(f"Pausing before next attack (config: nap_time): {self.experiment_config.get_nap_time()}", 2)
time.sleep(self.experiment_config.get_nap_time())
retries = 100
for target_system in self.targets:
running_agents = caldera_control.list_paws_of_running_agents()
self.attack_logger.vprint(f"Agents currently connected to the server: {running_agents}", 2)
while target_system.get_paw() not in running_agents:
time.sleep(1)
if it_worked:
self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Restarting caldera server and waiting for clients to re-connect{CommandlineColors.ENDC}", 1)
self.attacker_1.start_caldera_server()
self.attack_logger.vprint(f"Pausing before next attack (config: nap_time): {self.experiment_config.get_nap_time()}", 2)
time.sleep(self.experiment_config.get_nap_time())
retries = 100
for target_system in self.targets:
running_agents = caldera_control.list_paws_of_running_agents()
retries -= 1
self.attack_logger.vprint(f"Waiting for clients to re-connect ({retries}, {running_agents}) ", 3)
if retries <= 0:
raise ServerError
self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Restarted caldera server clients re-connected{CommandlineColors.ENDC}", 1)
# End of fix
self.attack_logger.vprint(f"Agents currently connected to the server: {running_agents}", 2)
while target_system.get_paw() not in running_agents:
time.sleep(1)
running_agents = caldera_control.list_paws_of_running_agents()
retries -= 1
self.attack_logger.vprint(f"Waiting for clients to re-connect ({retries}, {running_agents}) ", 3)
if retries <= 0:
raise ServerError
self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Restarted caldera server clients re-connected{CommandlineColors.ENDC}", 1)
# End of fix
self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Finished Caldera attacks{CommandlineColors.ENDC}", 1)

@ -104,7 +104,7 @@ def create_parser():
parser_delete_agents.set_defaults(func=delete_agents)
# For all parsers
main_parser.add_argument("--caldera_url", help="caldera url, including port", default="http://192.168.178.118:8888/")
main_parser.add_argument("--caldera_url", help="caldera url, including port", default="http://192.168.178.125:8888/")
main_parser.add_argument("--apikey", help="caldera api key", default="ADMIN123")
return main_parser

@ -0,0 +1,13 @@
bd527b63-9f9e-46e0-9816-b8434d2b8989
c0da588f-79f0-4263-8998-7496b1a40596
c1cd6388-3ced-48c7-a511-0434c6ba8f48
feaced8f-f43f-452a-9500-a5219488abb8
b6f545ef-f802-4537-b59d-2cb19831c8ed
3b5db901-2cb8-4df7-8043-c4628a6a5d5a
530e47c6-8592-42bf-91df-c59ffbd8541b
b22b3b47-6219-4504-a2e6-ae8263e49fc3
2dece965-37a0-4f70-a391-0f30e3331aba
5c4dd985-89e3-4590-9b57-71fed66ff4e2
8c06ebf8-bacf-486b-bd77-21ba8c5a5777
ce485320-41a4-42e8-a510-f5a8fe96a644
b007fc38-9eb7-4320-92b3-9a3ad3e6ec25

@ -20,7 +20,19 @@ def run(args):
@param args: arguments from the argparse parser
"""
Experiment(args.configfile, args.verbose)
if args.caldera_attack_file:
with open(args.caldera_attack_file, "rt") as fh:
for line in fh:
line = line.strip()
print(f"Running calder attack {line}")
Experiment(args.configfile, args.verbose, [line])
else:
caldera_attack = None
if args.caldera_attack:
caldera_attack = [args.caldera_attack]
Experiment(args.configfile, args.verbose, caldera_attack)
def create_parser():
@ -35,6 +47,8 @@ def create_parser():
parser_run = subparsers.add_parser("run", help="run experiments")
parser_run.set_defaults(func=run)
parser_run.add_argument("--configfile", default="experiment.yaml", help="Config file to create from")
parser_run.add_argument("--caldera_attack", default=None, help="The id of a specific caldera attack to run")
parser_run.add_argument("--caldera_attack_file", default=None, help="The file name containing a list of caldera attacks to run")
return parser

@ -58,7 +58,9 @@ globs = ["TODO.md",
"plugins/avast_internal_plugins/*/*/hosts",
"plugins/README.md",
"pylint.rc",
"shipit_log.txt"]
"shipit_log.txt",
"all_caldera_attacks_unique.txt",
"caldera_subset.txt"]
try:
os.remove(filename)

Loading…
Cancel
Save