Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Artifact lookup soc #230

Merged
merged 7 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,9 @@ backend/report.pdf

backend/report.html
backend/app/integrations/office365/services/wazuh_config.xml

frontend/tsconfig.app.tsbuildinfo

frontend/tsconfig.node.tsbuildinfo

frontend/tsconfig.vitest.tsbuildinfo
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
"forgotpassword",
"Healthcheck",
"healthchecks",
"hoverable",
"iconoir",
"Indicies",
"Logsource",
"majesticons",
"mimecast",
"mynaui",
"nightwatch",
"ntime",
"nums",
"ochin",
Expand All @@ -41,8 +43,11 @@
"Socfortress",
"sparkline",
"Sysmon",
"tabler",
"taze",
"timerange",
"timesec",
"tsbuildinfo",
"uvicorn",
"venv",
"virustotal",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,9 @@ class IrisAlertPayload(BaseModel):
description="Contextual information about the alert",
)

# Allow extra fields
class Config:
extra = Extra.allow

def to_dict(self):
return self.dict(exclude_none=True)
129 changes: 123 additions & 6 deletions backend/app/integrations/alert_escalation/services/escalate_alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from app.connectors.dfir_iris.utils.universal import initialize_client_and_alert
from app.connectors.utils import get_connector_info_from_db
from app.connectors.wazuh_indexer.utils.universal import create_wazuh_indexer_client
from app.db.universal_models import Agents
from app.integrations.alert_creation.general.schema.alert import IrisAsset
from app.integrations.alert_creation_settings.models.alert_creation_settings import (
AlertCreationSettings,
)
Expand All @@ -23,6 +25,7 @@
from app.integrations.alert_escalation.schema.escalate_alert import IrisAlertPayload
from app.integrations.alert_escalation.schema.escalate_alert import SourceFieldsToRemove
from app.integrations.alert_escalation.schema.escalate_alert import SyslogLevelMapping
from app.integrations.utils.alerts import get_asset_type_id


async def fetch_settings(field: str, value: str, session: AsyncSession):
Expand Down Expand Up @@ -131,22 +134,62 @@ async def set_alert_level(syslog_level: str):
return 3


async def get_process_name(source_dict: dict) -> List[str]:
def remove_process_name_if_osquery(source_dict: dict) -> None:
"""
Get the process name from the source dictionary.
Remove the process_name field from the source dictionary if rule_group1 is 'osquery'.

Args:
source_dict (dict): The source dictionary.
"""
rule_group1 = source_dict.get("rule_group1")
if rule_group1 == "osquery":
logger.info("Removing process_name field")
source_dict.pop("process_name", None)


def get_process_image(source_dict: dict) -> str:
"""
Get the process_image field from the source dictionary.

Args:
source_dict (dict): The source dictionary.

Returns:
List[str]: The process name as a list.
str: The process image.
"""
# Get the last part of the process_image path
process_image = source_dict.get("process_image")
if process_image is None:
process_image = source_dict.get("data_win_eventdata_image")
logger.info(f"Process image: {process_image}")
return process_image if process_image else source_dict.get("data_win_eventdata_image")


def get_process_name_from_image(process_image: str) -> str:
"""
Get the process name from the process image.

Args:
process_image (str): The process image.

Returns:
str: The process name.
"""
process_name = os.path.basename(process_image) if process_image else None
logger.info(f"Process name: {process_name}")
return process_name


async def get_process_name(source_dict: dict) -> List[str]:
"""
Get the process name from the source dictionary.

Args:
source_dict (dict): The source dictionary.

Returns:
List[str]: The process name as a list.
"""
remove_process_name_if_osquery(source_dict)
process_image = get_process_image(source_dict)
process_name = get_process_name_from_image(process_image)
return [process_name] if process_name else []


Expand Down Expand Up @@ -314,6 +357,9 @@ async def build_alert_payload(
# Get the timefield value from the alert_details
if hasattr(alert_details, timefield):
alert_details.time_field = getattr(alert_details, timefield)
# Check if its part of _source
if hasattr(alert_details._source, timefield):
alert_details.time_field = getattr(alert_details._source, timefield)
logger.info(f"Alert has context: {context_payload}")
try:
return IrisAlertPayload(
Expand All @@ -335,6 +381,75 @@ async def build_alert_payload(
)


async def retrieve_agent_details_from_db(agent_name: str, session: AsyncSession):
"""
Retrieve agent details from the database.

Args:
agent_name (str): The name of the agent.
session (AsyncSession): The database session.

Returns:
Agents: The agent details.
"""
logger.info(f"Retrieving agent details for {agent_name}")
result = await session.execute(
select(Agents).where(Agents.hostname == agent_name),
)
agent = result.scalars().first()
if agent:
return agent
return None


async def add_asset_to_iris_alert(alert_id: int, asset_details: Agents, iris_alert_payload: IrisAlertPayload, session: AsyncSession):
client, alert_client = await initialize_client_and_alert("DFIR-IRIS")
asset_data = {
"assets": [
dict(
IrisAsset(
asset_name=asset_details.hostname,
asset_ip=asset_details.ip_address,
asset_description="",
asset_type_id=await get_asset_type_id(asset_details.os),
asset_tags=f"agent_id:{asset_details.agent_id}",
).dict(),
),
],
}
logger.info(f"Adding asset to alert {alert_id} with asset data: {asset_data}")
await fetch_and_validate_data(
client,
alert_client.update_alert,
alert_id,
asset_data,
)


async def add_asset_if_wazuh(alert_details: GenericAlertModel, alert_id: int, iris_alert_payload: IrisAlertPayload, session: AsyncSession):
"""
Adds the asset to the alert if the alert is from Wazuh.

Args:
alert_details (GenericAlertModel): The details of the alert.
alert_id (int): The ID of the alert.
session (AsyncSession): The database session.
"""
# Check if `agent_id` is present in the alert details and is not equal to `000`
if hasattr(alert_details._source, "agent_id") and alert_details._source.agent_id != "000":
logger.info(f"Adding asset to alert {alert_id}")
asset_details = await retrieve_agent_details_from_db(alert_details._source.agent_name, session)
if asset_details:
await add_asset_to_iris_alert(alert_id, asset_details, iris_alert_payload, session)
logger.info(f"Asset added to alert {alert_id}")
return None
else:
logger.error(f"Failed to retrieve asset details for {alert_details._source.agent_name}")
return None
logger.info(f"Alert {alert_id} is not from Wazuh. Skipping asset addition.")
return None


async def create_alert(
alert: CreateAlertRequest,
session: AsyncSession,
Expand Down Expand Up @@ -373,6 +488,8 @@ async def create_alert(
)
alert_id = result["data"]["alert_id"]

await add_asset_if_wazuh(alert_details, alert_id, iris_alert_payload, session)

es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
iris_url = await add_alert_to_document(
es_client,
Expand Down
2 changes: 1 addition & 1 deletion frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
extends: ["plugin:vue/vue3-essential", "eslint:recommended", "@vue/eslint-config-typescript"],
overrides: [
{
files: ["cypress/e2e/**.{cy,spec}.{js,ts,jsx,tsx}"],
files: ["cypress/e2e/**.{cy,spec}.{js,ts,jsx,tsx}", "cypress/support/**/*.{js,ts,jsx,tsx}"],
extends: ["plugin:cypress/recommended"]
}
],
Expand Down
Loading
Loading