blob: 220ffd52d750935d2613b5c760fd8bfefb1eb956 [file]
from base64 import b64decode
from datetime import timedelta
from app.objects.c_ability import Ability
from app.objects.c_agent import Agent
from app.objects.secondclass.c_executor import Executor
from app.objects.secondclass.c_fact import Fact
from app.utility.base_world import BaseWorld
class TestAgent:
def test_task_no_facts(self, event_loop, data_svc, obfuscator, init_base_world):
executor = Executor(name='psh', platform='windows', command='whoami')
ability = Ability(ability_id='123', executors=[executor])
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows')
event_loop.run_until_complete(agent.task([ability], obfuscator='plain-text'))
assert 1 == len(agent.links)
def test_task_missing_fact(self, event_loop, obfuscator, init_base_world):
executor = Executor(name='psh', platform='windows', command='net user #{domain.user.name} /domain')
ability = Ability(ability_id='123', executors=[executor])
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows')
event_loop.run_until_complete(agent.task([ability], obfuscator='plain-text'))
assert 0 == len(agent.links)
def test_task_with_facts(self, event_loop, obfuscator, init_base_world, knowledge_svc, fire_event_mock):
executor = Executor(name='psh', platform='windows', command='net user #{domain.user.name} /domain')
ability = Ability(ability_id='123', executors=[executor])
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows')
fact = Fact(trait='domain.user.name', value='bob')
event_loop.run_until_complete(agent.task([ability], 'plain-text', [fact]))
assert 1 == len(agent.links)
def test_builtin_fact_replacement(self, event_loop, obfuscator, init_base_world):
executor = Executor(name='psh', platform='windows',
command='echo #{paw} #{server} #{group} #{location} #{exe_name} #{upstream_dest}')
ability = Ability(ability_id='123', executors=[executor])
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows',
group='my_group', server='http://localhost:8888', location='testlocation', exe_name='testexe')
event_loop.run_until_complete(agent.task([ability], 'plain-text', []))
assert 1 == len(agent.links)
link = agent.links[0]
decoded_command = b64decode(link.command).decode('utf-8')
want = 'echo 123 http://localhost:8888 my_group testlocation testexe http://localhost:8888'
assert want == decoded_command
def test_builtin_fact_replacement_with_upstream_dest(self, event_loop, obfuscator, init_base_world):
executor = Executor(name='psh', platform='windows',
command='echo #{paw} #{server} #{group} #{location} #{exe_name} #{upstream_dest}')
ability = Ability(ability_id='123', executors=[executor])
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows',
group='my_group', server='http://10.10.10.10:8888', location='testlocation', exe_name='testexe',
upstream_dest='http://127.0.0.1:12345')
event_loop.run_until_complete(agent.task([ability], 'plain-text', []))
assert 1 == len(agent.links)
link = agent.links[0]
decoded_command = b64decode(link.command).decode('utf-8')
want = 'echo 123 http://10.10.10.10:8888 my_group testlocation testexe http://127.0.0.1:12345'
assert want == decoded_command
def test_preferred_executor_psh(self, event_loop, ability, executor):
executor_test = executor(name='test', platform='windows')
executor_cmd = executor(name='cmd', platform='windows')
executor_psh = executor(name='psh', platform='windows')
test_ability = ability(ability_id='123', executors=[executor_test, executor_cmd, executor_psh])
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['psh', 'cmd'], platform='windows')
preferred_executor = event_loop.run_until_complete(agent.get_preferred_executor(test_ability))
assert preferred_executor is executor_psh # 'psh' preferred if available
def test_preferred_executor_from_agent_executor(self, event_loop, ability, executor):
executor_test = executor(name='test', platform='windows')
executor_cmd = executor(name='cmd', platform='windows')
executor_psh = executor(name='psh', platform='windows')
test_ability = ability(ability_id='123', executors=[executor_test, executor_cmd, executor_psh])
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows')
preferred_executor = event_loop.run_until_complete(agent.get_preferred_executor(test_ability))
assert preferred_executor is executor_cmd # prefer agent's first executor, not ability's
def test_set_pending_executor_path_update(self):
original_executors = ['cmd', 'test']
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=original_executors, platform='windows')
executor_to_change = 'test'
new_path = 'new_path'
want = dict(action='update_path', executor=executor_to_change, value=new_path)
assert agent.executor_change_to_assign is None
agent.set_pending_executor_path_update(executor_to_change, new_path)
assert agent.executor_change_to_assign == want
assert agent.executors == original_executors
def test_assign_executor_change(self):
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows')
executor_to_change = 'test'
new_path = 'new_path'
want = dict(action='update_path', executor=executor_to_change, value=new_path)
agent.set_pending_executor_path_update(executor_to_change, new_path)
assert agent.assign_pending_executor_change() == want
assert agent.executor_change_to_assign is None
def test_set_pending_executor_removal(self):
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows')
executor_to_remove = 'test'
want = dict(executor=executor_to_remove, action='remove')
agent.set_pending_executor_removal(executor_to_remove)
assert agent.executor_change_to_assign == want
assert agent.executors == ['cmd']
def test_removing_nonexistent_executor(self):
original_executors = ['cmd', 'test']
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=original_executors, platform='windows')
agent.set_pending_executor_removal('idontexist')
assert agent.executor_change_to_assign is None
assert agent.executors == original_executors
def test_updating_nonexistent_executor(self):
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows')
agent.set_pending_executor_path_update('idontexist', 'fakepath')
assert agent.executor_change_to_assign is None
def test_heartbeat_modification_during_pending_executor_removal(self, event_loop):
original_executors = ['cmd', 'test']
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=original_executors, platform='windows')
agent.set_pending_executor_removal('test')
event_loop.run_until_complete(agent.heartbeat_modification(executors=original_executors))
assert agent.executors == ['cmd']
def test_status_and_kill(self, event_loop, mocker, mock_time):
BaseWorld.set_config(name='agents', prop='untrusted_timer', value=30)
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd'], platform='windows')
assert agent.status == 'alive'
event_loop.run_until_complete(agent.kill())
assert agent.status == 'pending kill'
assert agent.watchdog == 1 and agent.sleep_min == 3 and agent.sleep_max == 3
event_loop.run_until_complete(agent.heartbeat_modification())
assert agent.status == 'dead'
with mocker.patch('app.objects.c_agent.datetime') as mock_datetime:
mock_datetime.return_value = mock_datetime
mock_datetime.now.return_value = mock_time
second_agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd'], platform='windows')
event_loop.run_until_complete(second_agent.kill())
mock_datetime.now.return_value = mock_time + timedelta(0, 10)
assert second_agent.status == 'pending kill'
mock_datetime.now.return_value = mock_time + timedelta(0, 32)
assert second_agent.status == 'pending kill'
mock_datetime.now.return_value = mock_time + timedelta(0, 34)
assert second_agent.status == 'dead'
def test_status_and_timeout(self, event_loop, mocker, mock_time):
BaseWorld.set_config(name='agents', prop='untrusted_timer', value=30)
with mocker.patch('app.objects.c_agent.datetime') as mock_datetime:
mock_datetime.return_value = mock_datetime
mock_datetime.now.return_value = mock_time
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd'], platform='windows')
assert agent.status == 'alive'
mock_datetime.now.return_value = mock_time + timedelta(0, 30)
assert agent.status == 'alive'
mock_datetime.now.return_value = mock_time + timedelta(0, 39)
assert agent.status == 'dead'
def test_store_new_agent(self, data_svc):
agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows')
stored_agent = agent.store(data_svc.ram)
assert len(data_svc.ram['agents']) == 1
assert agent in data_svc.ram['agents']
assert stored_agent == agent
def test_store_existing_agent(self, data_svc):
agent = Agent(paw='123', sleep_min=2, group='red', sleep_max=8, watchdog=0, executors=['cmd', 'test'],
platform='windows', trusted=True, pending_contact='HTML')
agent.store(data_svc.ram)
assert len(data_svc.ram['agents']) == 1
agent.group = "blue"
agent.trusted = False
agent.sleep_min = 1
agent.sleep_max = 5
agent.watchdog = 1
agent.pending_contact = "HTTP"
stored_agent = agent.store(data_svc.ram)
assert len(data_svc.ram['agents']) == 1
assert stored_agent.schema.dump(stored_agent) == agent.schema.dump(agent)