利用 Elastic API 实现自定义 AI 驱动的 SOAR

作者:来自 Elastic Kevin Umsted

安全团队面临着无休止的警报、复杂的调查以及资源有限的艰巨挑战。这不仅是要发现威胁,还要快速且高效地应对。Elastic Security 一直提供用于检测、调查和响应的预构建能力。但真正让 Elastic 脱颖而出的是其开放、以 API 为先的方式,使你能够在安全运营中心(SOC)构建并自动化特定的工作流程。

在这篇博客中,我们将展示 Elastic 可扩展 API 如何通过一个常见的 “实例” 解锁 AI 驱动的安全编排、自动化与响应(security orchestration, automation, and response - SOAR)新可能性。你将学习如何结合 Elastic 的安全平台、AI 以及像 Slack 这样的协作工具来设计、自动化并持续完善你自己的响应剧本。

利用 Elastic API 强化你的工作流程

在这个场景中,SOC 的检测工程团队正面临一个熟悉的难题:未能满足关键警报的服务水平目标。尽管团队已部署了强大的工具来提高平均响应时间(mean time to respond - MTTR),但仍在努力应对庞大而紧迫的警报量。因此,一些关键警报有时会被延迟处理,甚至未能在预期时间内解决。为了解决这一问题,团队希望借助自动化、AI 以及 Elastic 强大的 API 进一步优化工作流程。

现在,想象这样一个工作流程:每个关键警报都能被一致地检测、分类并响应,分析人员能全程引导并监督每一步。以下是如何使用 Elastic 灵活的 API 和像 Slack 这样的常用协作工具实现这一解决方案。

我们来分解这个项目的步骤:

  • 使用 Elasticsearch Python 客户端监控一个 Elastic 集群。

  • 监控任何被标记为关键的警报。

  • 将警报 JSON 通过 API 发送给 Elastic AI Assistant for Security。

  • 获取 AI 响应。

  • 将 AI 响应发布到 Slack 频道。

  • 在 Slack 消息中标记相关安全分析人员,以通知他们并在 Slack 中直接进行操作。

事件时间线

让我们来梳理一下事件的时间线。首先,Elastic Security 收到一个关键警报,涉及一台受端点检测与响应(Elastic Defend)保护的重要 Windows 服务器。在这个案例中,Elastic 检测到 Windows Management Instrumentation (WMI) 被用来创建注册表项,从而实现对该关键服务器的持久访问。

Elastic Security 警报

Python 可以每分钟查询一次 Elastic,以获取任何关键警报。当发现此类警报时,将提取警报的完整 JSON 有效负载以进行处理。下面是一个可用于实时监控 Elastic 中关键警报的 Python 示例:

es = Elasticsearch(
    os.getenv("ELASTICSEARCH_URL"),
    api_key=ELASTICSEARCH_API_KEY
)

def monitor_alerts():
    from datetime import datetime, timedelta, timezone
    import time

    polling_interval = 60  # Poll every 60 seconds
    index_patterns = {
        "endpoint": {
            "pattern": ".ds-logs-endpoint.alerts*",
            "timestamp_field": "Responses.@timestamp"
        },
        "security": {
            "pattern": ".internal.alerts-security.alerts-default-*",
            "timestamp_field": "@timestamp"
        }
    }

    last_timestamps = {
        key: datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
        for key in index_patterns
    }

    while True:
        for key, config in index_patterns.items():
            pattern = config["pattern"]
            ts_field = config["timestamp_field"]

            query = {
                "query": {
                    "bool": {
                        "filter": [
                            {"range": {ts_field: {"gt": last_timestamps[key]}}},
                            {"term": {"kibana.alert.severity": "critical"}}
                        ]
                    }
                },
                "sort": [{ts_field: {"order": "asc"}}]
            }

            try:
                response = es.search(index=pattern, body=query)
                alerts = response['hits']['hits']
                if alerts:
                    print(f"Found {len(alerts)} critical alert(s) for {key} since {last_timestamps[key]}")
                    max_ts = last_timestamps[key]
                    for alert in alerts:
                        alert_id = alert.get('_id')
                        if alert_id in processed_alert_ids:
                            continue
                        processed_alert_ids.add(alert_id)

                        alert_json = alert['_source']
                        process_alert(alert_json)

                        alert_ts = get_custom_alert_timestamp(alert_json, ts_field)
                        if alert_ts and alert_ts > max_ts:
                            max_ts = alert_ts
                    dt = datetime.fromisoformat(max_ts.replace("Z", "+00:00"))
                    dt += timedelta(seconds=1)
                    last_timestamps[key] = dt.isoformat().replace("+00:00", "Z")
            except Exception as e:
                print(f"Error fetching critical alerts for {key}: {str(e)}")

        time.sleep(polling_interval)

捕获的 JSON 然后通过 API 请求发送到 Elastic AI Assistant for Security。一个 prompt 会附加到该 JSON,以为 AI Assistant 提供生成适当响应所需的上下文。

Python prompt 代码

def process_alert(alert_json):
    alert_str = json.dumps(alert_json, indent=2)
    prompt = (
        f"Please look at this JSON:\n{alert_str}\n\n"
        "You are a security analyst and you want to run commands to start a forensic investigation for this alert. "
        "Make sure you summarize the alert in one paragraph. "
        "Summarize the alert and then give 5 commands that you would run to start your IR investigation. "
        "The next 3 commands should be dedicated to commands that would remediate the malware or malicious activity. "
        "The total commands should be 8. Make sure you include the affected host via the IP address in the alert summary. "
        "The commands should be in a list format and clearly separated into Investigation Commands and Remediation Commands. For example: \n"
        "Investigation Commands:\n1. command\n2. command\n... \nRemediation Commands:\n1. command\n2. command\n..."
    )    print("Alert Received")
    response = chat_complete(prompt)
    print("AI Responded")

Python API 调用

def chat_complete(question, thread_ts=None, use_existing_conversation=False):
    url = f"{KIBANA_URL}/api/security_ai_assistant/chat/complete"
    headers = {
        "kbn-xsrf": "true",
        "Content-Type": "application/json",
    }
    
    payload = {
        "messages": [{"role": "user", "content": question}],
        "connectorId": CONNECTOR_ID,
        "persist": True
    }

    if use_existing_conversation and thread_ts and thread_ts in conversation_map:
        payload["conversationId"] = conversation_map[thread_ts]

    response = requests.post(
        url,
        auth=(USERNAME, PASSWORD),
        headers=headers,
        json=payload,
        stream=True
    )
    response.raise_for_status()

    full_response = ""
    for line in response.iter_lines():
        if line:
            decoded_line = line.decode('utf-8')
            try:
                event = json.loads(decoded_line)
                if "data" in event:
                    full_response += event["data"]
                if "conversationId" in event and thread_ts:
                    conversation_map[thread_ts] = event["conversationId"]
            except json.JSONDecodeError:
                continue
    
    return full_response.strip()

AI 助手接收 API 请求并使用大型语言模型(LLM)生成响应。这个响应包括一个可操作的警报摘要以及推荐的调查和缓解命令。

AI 助手还可以确定应该在 Slack 消息中标记哪位分析师。它通过查询一个包含 SOC 中分析师信息的自定义知识库来实现这一点。这个自定义知识功能非常强大,大幅提升了 AI 助手的有效性。

通过在自定义知识库中集中机构知识,AI 助手最大限度地减少了获取关键信息的延迟。这提升了整个 SOC 的操作工作流程的速度和一致性。

了解如何设置自定义 AI 助手知识库

一旦 AI 助手返回其响应,使用 Python 将消息及相应的分析人员 @ 提及发送到指定的 Slack 频道。

将响应发送到 Slack

response = chat_complete(prompt)
print("AI Responded")

if response:
    clean_response = response.replace("**", "") 

    inv_match = re.search(r"(?i)\s*Investigation Commands:\s*", clean_response)
    rem_match = re.search(r"(?i)\s*Remediation Commands:\s*", clean_response)

    if not inv_match or not rem_match:
        print("Could not find both 'Investigation Commands:' and 'Remediation Commands:' in the response.")
        return

    summary = clean_response[:inv_match.start()].strip()
    inv_commands_part = clean_response[inv_match.end():rem_match.start()].strip()
    rem_commands_part = clean_response[rem_match.end():].strip()

    inv_commands = re.findall(r"^\s*\d+\.\s+(.+)$", inv_commands_part, re.MULTILINE)
    rem_commands = re.findall(r"^\s*\d+\.\s+(.+)$", rem_commands_part, re.MULTILINE)

    # Extract the handler from AI (e.g., "Who handles critical windows alerts?")
    handler_question = f"Who handles {operating_system.lower()} alerts? Respond with just their name."
    handler_response = chat_complete(handler_question).strip()
    handler_id = get_user_id_by_name(handler_response)
    handler_mention = f"<@{handler_id}>" if handler_id else handler_response

    full_message = (
        f"New Alert Detected\n\n{clean_response}\n\n"
        f"Alert assigned to:\n{handler_mention}\n\n"
        "Do you want me to run the investigation and remediation commands?"
    )

    blocks = [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": full_message
            }
        },
        {
            "type": "actions",
            "elements": [
                {
                    "type": "button",
                    "text": {"type": "plain_text", "text": "Yes"},
                    "action_id": "run_commands",
                    "value": "run"
                },
                {
                    "type": "button",
                    "text": {"type": "plain_text", "text": "No"},
                    "action_id": "do_not_run",
                    "value": "do_not_run"
                }
            ]
        }
    ]

    result = app.client.chat_postMessage(
        channel=ALERT_CHANNEL,
        blocks=blocks,
        text="New Alert Detected"
    )

    alert_data_map[result['ts']] = {
        "alert_json": alert_json,
        "investigation_commands": inv_commands,
        "remediation_commands": rem_commands,
        "host_ip": host_ip,
        "ai_summary": summary
    }

Slack 消息:

此阶段,Slack 消息会生成,包含警报的有意义摘要、受影响的主机,以及建议的调查和修复命令列表。分配给该警报的分析人员会在消息中被 @ 提及,并且会看到“是/否”选项,用于批准在主机上执行命令。

在此场景中,分析人员点击“是”并批准自动响应。

为了执行命令,使用 Python 和 Windows 远程管理(WinRM)。受影响主机的 IP 地址和命令列表会从 AI 助手的响应中提取,并按顺序在目标机器上执行。

Python 命令执行

import winrm

def execute_winrm_command(host_ip: str, command: str, winrm_domain: str, winrm_username: str, winrm_password: str) -> str:
    session = winrm.Session(
        host_ip,
        auth=(f"{winrm_domain}\\{winrm_username}", winrm_password),
        transport='ntlm'
    )
    full_cmd = (
        "$ProgressPreference = 'SilentlyContinue'; "
        "$VerbosePreference = 'SilentlyContinue'; "
        "$WarningPreference = 'SilentlyContinue'; "
        f"try {{ {command} | Format-List | Out-String -Width 120 }} "
        "catch {{ 'Error: ' + $_.Exception.Message }}"
    )
    result = session.run_ps(full_cmd)
    output = result.std_out.decode('utf-8', errors='ignore').strip()
    return output if output else "No output returned."

def execute_winrm_commands(host_ip: str, commands: list, winrm_domain: str, winrm_username: str, winrm_password: str) -> dict:

    outputs = {}
    session = winrm.Session(
        host_ip,
        auth=(f"{winrm_domain}\\{winrm_username}", winrm_password),
        transport='ntlm'
    )
    for command in commands:
        full_cmd = (
            "$ProgressPreference = 'SilentlyContinue'; "
            "$VerbosePreference = 'SilentlyContinue'; "
            "$WarningPreference = 'SilentlyContinue'; "
            f"try {{ {command} | Format-List | Out-String -Width 120 }} "
            "catch {{ 'Error: ' + $_.Exception.Message }}"
        )
        result = session.run_ps(full_cmd)
        output = result.std_out.decode('utf-8', errors='ignore').strip()
        outputs[command] = output if output else "No output returned."
    return outputs

分析人员执行的命令:

每个操作都被完整跟踪和可审计。应用程序为每个事件在 Elastic Security 中创建一个新案例,并附上所有 AI 摘要、执行的命令以及响应过程中生成的所有输出,确保所有证据都被保存以备将来参考或合规需求。

Python 创建案例代码

def create_case(alert_json, command_output, ai_summary):
    description = ai_summary + "\n\n"
    if command_output:
        description += "Commands Executed:\n\n"
        for cmd, output in command_output.items():
            truncated_output = output[:500] + "..." if len(output) > 500 else output
            description += f"Command: `{cmd}`\n\nOutput:\n```\n{truncated_output}\n```\n\n"

    if len(description) > 30000:
        description = description[:29997] + "..."

    title = "Investigation for Alert"

    case_payload = {
        "title": title,
        "description": description,
        "tags": ["auto-generated", "investigation"],
        "connector": {"id": "none", "name": "none", "type": ".none", "fields": None},
        "owner": "securitySolution",
        "settings": {"syncAlerts": True},
        "severity": "medium"
    }

    url = f"{KIBANA_URL}/api/cases"
    headers = {"kbn-xsrf": "true", "Content-Type": "application/json"}
    
    try:
        response = requests.post(
            url,
            auth=(USERNAME, PASSWORD),
            headers=headers,
            json=case_payload
        )
        if response.status_code == 200:
            case_id = response.json().get('id')
            print(f"Successfully created case: {case_id}")
            return case_id
        else:
            print(f"Failed to create case: {response.status_code} - {response.text}")
            return None
    except requests.RequestException as e:
        print(f"Request failed: {str(e)}")
        return None

Kibana 安全案例:

Elastic 提供无与伦比的灵活性

Elastic Security 不仅限于开箱即用的检测和响应,还支持无缝集成企业 AI 和协作工具(如 Slack)的定制自动化流程。

利用 Elastic 强大的 APIs 和开源基础,我们能够:

  • 编程式监控关键警报

  • 使用 Elastic AI Assistant for Security 生成富有上下文的可操作响应

  • 以可控、可审计的方式自动化分类和补救

  • 直接集成到分析师每天使用的协作工具中

Elastic 开放、API 优先的架构为安全团队提供了无与伦比的灵活性,可以构建最适合他们的工作流程。这就是基于 Elastic 构建的力量。无论你是通过 AI 扩展响应能力,自定义警报工作流程,还是将 Elastic 嵌入现有的 SecOps 堆栈,平台都设计成以你需要的方式工作。

Elastic 为 AI 驱动的 SOAR 提供无与伦比的灵活性

准备好自己动手了吗?

开始使用 Elastic AI Assistant API 文档构建你的工作流,或者联系我们深入了解 Elastic 开放平台如何满足你独特的 SOC 需求。

本文中描述的任何功能或特性的发布时间和内容,完全由 Elastic 自行决定。任何当前不可用的功能可能无法按时或根本不会发布。

本文中可能使用或提及了第三方生成式 AI 工具,这些工具由各自所有者拥有和运营。Elastic 无法控制这些第三方工具,对其内容、操作或使用不承担任何责任,也不对你使用这些工具可能导致的任何损失或损害负责。使用 AI 工具处理个人、敏感或机密信息时请谨慎。你提交的任何数据可能会被用于 AI 训练或其他目的。无法保证你提供的信息会被安全或保密地保存。你应在使用前熟悉任何生成式 AI 工具的隐私政策和使用条款。

Elastic、Elasticsearch 及相关标志是 Elasticsearch N.V. 在美国及其他国家的商标、徽标或注册商标。所有其他公司和产品名称均为其各自所有者的商标、徽标或注册商标。

原文:Harnessing Elastic APIs for custom AI-driven SOAR | Elastic Blog

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值