diff --git a/backend/app/api/c2.py b/backend/app/api/c2.py index 745637c..3f2a131 100644 --- a/backend/app/api/c2.py +++ b/backend/app/api/c2.py @@ -84,7 +84,7 @@ def upsert_c2_config(eid: int): if not parsed.hostname: return jsonify({"error": "url must contain a hostname"}), 400 - verify_tls = data.get("verify_tls", True) + verify_tls = data.get("verify_tls", False) if not isinstance(verify_tls, bool): return jsonify({"error": "verify_tls must be a boolean"}), 400 diff --git a/backend/app/models/c2_config.py b/backend/app/models/c2_config.py index 4015a13..e3876a0 100644 --- a/backend/app/models/c2_config.py +++ b/backend/app/models/c2_config.py @@ -19,7 +19,7 @@ class C2Config(db.Model): # type: ignore[name-defined] ) url = db.Column(db.Text, nullable=False) api_token_encrypted = db.Column(db.Text, nullable=False) - verify_tls = db.Column(db.Boolean, nullable=False, default=True) + verify_tls = db.Column(db.Boolean, nullable=False, default=False) created_at = db.Column( db.DateTime, nullable=False, default=lambda: datetime.now(UTC) ) diff --git a/backend/app/services/c2/factory.py b/backend/app/services/c2/factory.py index 0c46370..75cce7a 100644 --- a/backend/app/services/c2/factory.py +++ b/backend/app/services/c2/factory.py @@ -6,7 +6,7 @@ import os from backend.app.services.c2.adapter import C2Adapter -def get_adapter(url: str, api_token: str, verify_tls: bool = True) -> C2Adapter: +def get_adapter(url: str, api_token: str, verify_tls: bool = False) -> C2Adapter: """Return the correct C2Adapter based on MIMIC_C2_ADAPTER (default: mythic).""" adapter_name = os.environ.get("MIMIC_C2_ADAPTER", "mythic").lower() diff --git a/backend/app/services/c2/mythic.py b/backend/app/services/c2/mythic.py index 2eec290..c430afa 100644 --- a/backend/app/services/c2/mythic.py +++ b/backend/app/services/c2/mythic.py @@ -15,6 +15,8 @@ from __future__ import annotations from datetime import datetime import requests +import urllib3 +from urllib3.exceptions import InsecureRequestWarning from backend.app.services.c2.adapter import ( C2Adapter, @@ -113,10 +115,14 @@ query GetTaskOutput($display_id: Int!) { class MythicAdapter(C2Adapter): """Real Mythic 3.x adapter using GraphQL over HTTP.""" - def __init__(self, url: str, api_token: str, verify_tls: bool = True) -> None: + def __init__(self, url: str, api_token: str, verify_tls: bool = False) -> None: self._url = url.rstrip("/") + "/graphql" self._token = api_token self._verify = verify_tls + if not verify_tls: + # Lab/red-team Mythic instances commonly use self-signed certs. + # Suppress urllib3 warnings once per process when verification is off. + urllib3.disable_warnings(InsecureRequestWarning) def _headers(self) -> dict[str, str]: return { diff --git a/backend/migrations/versions/0008_c2_verify_tls_default_false.py b/backend/migrations/versions/0008_c2_verify_tls_default_false.py new file mode 100644 index 0000000..33e1dc0 --- /dev/null +++ b/backend/migrations/versions/0008_c2_verify_tls_default_false.py @@ -0,0 +1,33 @@ +"""flip c2_config.verify_tls server_default to false + +Revision ID: 0008_c2_verify_tls_default_false +Revises: 0007 +Create Date: 2026-06-21 00:00:00.000000 +""" +import sqlalchemy as sa +from alembic import op + +revision = "0008_c2_verify_tls_default_false" +down_revision = "0007" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + with op.batch_alter_table("c2_config") as batch_op: + batch_op.alter_column( + "verify_tls", + existing_type=sa.Boolean(), + existing_nullable=False, + server_default=sa.false(), + ) + + +def downgrade() -> None: + with op.batch_alter_table("c2_config") as batch_op: + batch_op.alter_column( + "verify_tls", + existing_type=sa.Boolean(), + existing_nullable=False, + server_default=sa.true(), + ) diff --git a/backend/tests/test_c2_config.py b/backend/tests/test_c2_config.py index fd2c617..d8f4235 100644 --- a/backend/tests/test_c2_config.py +++ b/backend/tests/test_c2_config.py @@ -206,13 +206,13 @@ def test_get_config_returns_has_token_not_cleartext( assert "s3cr3t" not in str(body) -def test_get_config_verify_tls_default_true( +def test_get_config_verify_tls_default_false( client: FlaskClient, admin_token: str ) -> None: eng = _make_engagement(client, admin_token) - _put_config(client, admin_token, eng["id"]) + _put_config(client, admin_token, eng["id"], verify_tls=False) resp = client.get(f"/api/engagements/{eng['id']}/c2-config", headers=_h(admin_token)) - assert resp.get_json()["verify_tls"] is True + assert resp.get_json()["verify_tls"] is False # ---------------------------------------------------------------------------