Skip to content

Commit

Permalink
Merge pull request #5 from BuildAndDestroy/redo
Browse files Browse the repository at this point in the history
Cleanup to match upstream master
  • Loading branch information
BuildAndDestroy authored Dec 4, 2023
2 parents d5d1cdc + 301eeb3 commit 2fd3dde
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 51 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/publish_docker_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
submodules: recursive

- name: Log in to the Container registry
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc
Expand Down
6 changes: 3 additions & 3 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- name: "MITRE Corporation"
title: "CALDERA: A Scalable, Automated Adversary Emulation Platform"
version: 4.1.0
date-released: 2022-09-17
title: "MITRE Caldera: A Scalable, Automated Adversary Emulation Platform"
version: 4.2.0
date-released: 2023-06-19
url: "https://github.com/mitre/caldera"
43 changes: 0 additions & 43 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,56 +24,13 @@ ENV VIRTUAL_ENV=/opt/venv/caldera
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"

# haproxy is needed for the ssl plugin
RUN apt-get install haproxy -y

# Generate self signed certificate
RUN openssl req -x509 -newkey rsa:4096 -out conf/certificate.pem -keyout conf/certificate.pem -subj "/C=US/ST=VA/L=McLean/O=Mitre/OU=IT/CN=mycaldera.caldera" -nodes
RUN mv conf/certificate.pem plugins/ssl/conf/certificate.pem

# Enable Self Signed Certificate within haproxy
RUN cp plugins/ssl/templates/haproxy.conf conf/
RUN sed -i 's#bind\ \*\:8443\ ssl\ crt\ plugins/ssl/conf/insecure_certificate.pem#bind\ \*\:8443\ ssl\ crt\ plugins/ssl/conf/certificate.pem#g' conf/haproxy.conf

# Enable ssl plugin
RUN sed -i '/^\-\ manx/a \-\ ssl' conf/default.yml

# Enable emu
#RUN sed -i '/^\-\ ssl/a \-\ emu' conf/default.yml

# haproxy is needed for the ssl plugin
RUN apt-get install haproxy -y

# Generate self signed certificate
RUN openssl req -x509 -newkey rsa:4096 -out conf/certificate.pem -keyout conf/certificate.pem -subj "/C=US/ST=VA/L=McLean/O=Mitre/OU=IT/CN=mycaldera.caldera" -nodes
RUN mv conf/certificate.pem plugins/ssl/conf/certificate.pem

# Enable Self Signed Certificate within haproxy
RUN cp plugins/ssl/templates/haproxy.conf conf/
RUN sed -i 's#bind\ \*\:8443\ ssl\ crt\ plugins/ssl/conf/insecure_certificate.pem#bind\ \*\:8443\ ssl\ crt\ plugins/ssl/conf/certificate.pem#g' conf/haproxy.conf

# Enable ssl plugin
RUN sed -i '/^\-\ manx/a \-\ ssl' conf/default.yml

# Enable emu
#RUN sed -i '/^\-\ ssl/a \-\ emu' conf/default.yml

# Install pip requirements
RUN pip3 install --no-cache-dir -r requirements.txt

# Set up config file and disable atomic by default
RUN python3 -c "import app; import app.utility.config_generator; app.utility.config_generator.ensure_local_config();"; \
sed -i '/\- atomic/d' conf/local.yml;

# Copy default.yml to local.yml or none of these changes above will stick
RUN cp conf/default.yml conf/local.yml

# Install golang
RUN curl -L https://go.dev/dl/go1.17.6.linux-amd64.tar.gz -o go1.17.6.linux-amd64.tar.gz
RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.6.linux-amd64.tar.gz;
ENV PATH="${PATH}:/usr/local/go/bin"
RUN go version;

# Compile default sandcat agent binaries, which will download basic golang dependencies.

# Install Go dependencies
Expand Down
1 change: 1 addition & 0 deletions app/contacts/contact_tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ async def send(self, session_id: int, cmd: str, timeout: int = 60) -> Tuple[int,
try:
conn = next(i.connection for i in self.sessions if i.id == int(session_id))
conn.send(str.encode(' '))
time.sleep(0.01)
conn.send(str.encode('%s\n' % cmd))
response = await self._attempt_connection(session_id, conn, timeout=timeout)
response = json.loads(response)
Expand Down
2 changes: 1 addition & 1 deletion plugins/debrief
2 changes: 1 addition & 1 deletion plugins/ssl
Submodule ssl updated 2 files
+7 −6 README.md
+0 −1 templates/haproxy.conf
99 changes: 99 additions & 0 deletions tests/objects/test_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,3 +497,102 @@ def test_update_untrusted_agents_with_untrusted_no_operation_agents(self, operat
op = Operation(name='test', agents=[], adversary=adversary)
op.update_untrusted_agents(operation_agent)
assert not op.untrusted_agents

def test_check_reason_skipped_unknown_platform(self, test_agent, test_ability):
test_agent.platform = 'unknown'
op = Operation(name='test', agents=[test_agent], state='running')
reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=test_agent.executors, agent_ran={})
assert reason['reason'] == 'Platform not available'
assert reason['reason_id'] == Operation.Reason.PLATFORM.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_check_reason_skipped_valid_executor(self, test_agent, test_ability):
test_agent.platform = 'darwin'
op = Operation(name='test', agents=[test_agent], state='running')
reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=[], agent_ran={})
assert reason['reason'] == 'Mismatched ability platform and executor'
assert reason['reason_id'] == Operation.Reason.EXECUTOR.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_check_reason_skipped_privilege(self, custom_agent, test_ability, mocker, test_executor):
test_executor.name = 'psh'
agent = custom_agent()
test_ability.privilege = 'Elevated'
op = Operation(name='test', agents=[agent], state='running')
reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=agent.executors, agent_ran={})
assert reason['reason'] == 'Ability privilege not fulfilled'
assert reason['reason_id'] == Operation.Reason.PRIVILEGE.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_check_reason_skipped_fact_dependency(self, custom_agent, test_ability, mocker, test_executor, fact):
test_executor.name = 'psh'
agent = custom_agent()
op = Operation(name='test', agents=[agent], state='running')
with mocker.patch('app.objects.c_ability.Ability.find_executors') as mock_find_executors:
mock_find_executors.return_value = [test_executor]
with mocker.patch('re.findall') as mock_findall:
mock_findall.return_value = [fact('test.fact.attribute')]
reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=agent.executors, agent_ran={})
assert reason['reason'] == 'Fact dependency not fulfilled'
assert reason['reason_id'] == Operation.Reason.FACT_DEPENDENCY.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_check_reason_skipped_link_ignored(self, custom_agent, test_ability, mocker, active_link):
agent = custom_agent()
op = Operation(name='test', agents=[agent], state='running')
test_link = Link.load(active_link)
op.chain = [test_link]
op.ignored_links = [test_link.id]
reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=agent.executors, agent_ran={})
assert reason['reason'] == 'Link ignored - highly visible or discarded link'
assert reason['reason_id'] == Operation.Reason.LINK_IGNORED.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_check_reason_skipped_untrusted(self, custom_agent, test_ability, mocker):
agent = custom_agent(trusted=False)
op = Operation(name='test', agents=[agent], state='running')
reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=agent.executors, agent_ran={})
assert reason['reason'] == 'Agent not trusted'
assert reason['reason_id'] == Operation.Reason.UNTRUSTED.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_check_reason_skipped_op_running(self, custom_agent, test_ability, mocker):
agent = custom_agent()
op = Operation(name='test', agents=[agent], state='running')
reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=agent.executors, agent_ran={})
assert reason['reason'] == 'Operation not completed'
assert reason['reason_id'] == Operation.Reason.OP_RUNNING.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_check_reason_skipped_other(self, custom_agent, test_ability, mocker):
agent = custom_agent()
op = Operation(name='test', agents=[agent], state='finished')
reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state,
agent_executors=agent.executors, agent_ran={})
assert reason['reason'] == 'Other'
assert reason['reason_id'] == Operation.Reason.OTHER.value
assert reason['ability_id'] == test_ability.ability_id
assert reason['ability_name'] == test_ability.name

async def test_add_ignored_link(self, make_test_link, operation_agent):
test_agent = operation_agent
test_link = make_test_link(9876, test_agent.paw, Link().states['DISCARD'])
op = Operation(name='test', agents=[test_agent], state='running')
op.add_ignored_link(test_link.id)
assert op.ignored_links
assert test_link.id in op.ignored_links
assert len(op.ignored_links) == 1

0 comments on commit 2fd3dde

Please sign in to comment.