diff --git a/.gitignore b/.gitignore index 1a02111a..ee0b85ed 100644 --- a/.gitignore +++ b/.gitignore @@ -397,3 +397,4 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml .DS_Store +.vscode/* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..b242572e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ] +} \ No newline at end of file diff --git a/Images/Community Plugins/DefenderDailyOperations/GetDefenderDevices.png b/Images/Community Plugins/DefenderDailyOperations/GetDefenderDevices.png new file mode 100644 index 00000000..780b102f Binary files /dev/null and b/Images/Community Plugins/DefenderDailyOperations/GetDefenderDevices.png differ diff --git a/Images/Community Plugins/DefenderDailyOperations/GetLatestEmailsByRecipient.png b/Images/Community Plugins/DefenderDailyOperations/GetLatestEmailsByRecipient.png new file mode 100644 index 00000000..3829932c Binary files /dev/null and b/Images/Community Plugins/DefenderDailyOperations/GetLatestEmailsByRecipient.png differ diff --git a/Images/Community Plugins/RedactPII/RedactPIIPrompt.png b/Images/Community Plugins/RedactPII/RedactPIIPrompt.png new file mode 100644 index 00000000..e0f580aa Binary files /dev/null and b/Images/Community Plugins/RedactPII/RedactPIIPrompt.png differ diff --git a/Images/Community Plugins/SecurityCopilotLogins/LoginsAndAnomalies.png b/Images/Community Plugins/SecurityCopilotLogins/LoginsAndAnomalies.png new file mode 100644 index 00000000..9f4f2c02 Binary files /dev/null and b/Images/Community Plugins/SecurityCopilotLogins/LoginsAndAnomalies.png differ diff --git a/Images/Community Plugins/SentinelIncidentComments/SentinelIncidentComments.png b/Images/Community Plugins/SentinelIncidentComments/SentinelIncidentComments.png new file mode 100644 index 00000000..e3137be1 Binary files /dev/null and b/Images/Community Plugins/SentinelIncidentComments/SentinelIncidentComments.png differ diff --git a/Images/Community Plugins/WatchlistKQL/TrustedUserWatchlist.png b/Images/Community Plugins/WatchlistKQL/TrustedUserWatchlist.png new file mode 100644 index 00000000..5c63cc27 Binary files /dev/null and b/Images/Community Plugins/WatchlistKQL/TrustedUserWatchlist.png differ diff --git a/Logic Apps/CfS-SendPromptbookResultsByEmail/CfS-SendPromptbookResultsByEmail.json b/Logic Apps/CfS-SendPromptbookResultsByEmail/CfS-SendPromptbookResultsByEmail.json new file mode 100644 index 00000000..92d0cd62 --- /dev/null +++ b/Logic Apps/CfS-SendPromptbookResultsByEmail/CfS-SendPromptbookResultsByEmail.json @@ -0,0 +1,1257 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "Send the results of Security Copilot's Promptbook by email", + "description": "This Logic App executes the specified Promptbook in Security Copilot (a.k.a. Copilot for Security). It then converts each prompt's response, written in the simplified markdown language of Security Copilot, into HTML. Using these HTML excerpts or plain text responses and starting from a specified HTML template, it constructs the HTML body of an email and finally sends that email to the specified recipients. It is possible to exclude one or more of the promptbook responses from the email by their position index (1, 2, 3, etc.)", + "prerequisites": "", + "postDeployment": [ + "1. Set the credentials for the OAuth API connection to Security Copilot and Office 365 (the connection to O365 is used to send the email).", + "2. Edit the workflow and set the promptbook name and related parameters in the run-prompt action (after the deployment, they are empty).", + "3. In the workflow editor, if needed, modify the trigger type and/or parameters (e.g., change the recurrence date and time or replace the trigger with the Microsoft Sentinel Incident trigger).", + "4. In the workflow editor, open the parameters and set the desired email title, subject, and recipients.", + "5. In the workflow editor, in the parameters list, if needed, specify the indexes of the prompts that should not be included in the email (use the SkipPromptsInOutput parameter).", + "6. In the workflow editor, in the parameters list, if needed, specify the prompts that should be replaced in the email (use the ReplacePromptsInOutput parameter).", + "7. In the workflow editor, if needed, further customize the email subject in the send email action (e.g., if you have changed the trigger to 'MS Sentinel Incident', you may want to dynamically include the incident ID or title at the end of the email subject; this cannot be done in the paramteres).", + "8. Enable the Logic App (the template deploys it in the disabled state)." + ], + "prerequisitesDeployTemplateFile": "", + "lastUpdateTime": "", + "entities": [ + ], + "tags": [ + ], + "support": { + "tier": "community", + "armtemplate": "Generated from https://github.com/Azure/Azure-Sentinel/tree/master/Tools/Playbook-ARM-Template-Generator" + }, + "author": { + "name": "Stefano Pescosolido (https://www.linkedin.com/in/stefanopescosolido/)" + } + }, + "parameters": { + "PlaybookName": { + "defaultValue": "CfS-SendPromptbookResultsByEmail", + "type": "string" + }, + "HtmlBodyHeader": { + "defaultValue": "
|
Name of CVE: @{items('For_each')?['cveID']}
CVE Date: @{items('For_each')?['dateAdded']}
Impacted Devices: @{body('Submit_a_Copilot_for_Security_prompt_2')?['EvaluationResultContent']}
Impacted Vendors: @{items('For_each')?['vendorProject']}
Details: @{body('Submit_a_Copilot_for_Security_prompt')?['EvaluationResultContent']}
Exploitation Status: @{items('For_each')?['knownRansomwareCampaignUse']}
Mediation Steps: @{items('For_each')?['requiredAction']}
@{body('Process_JSON')?['email_content']?['subject']}
\n\nBased on the analysis from this session, you will select one of the following categories to classify the email:
\nProvide detailed evidence and rationale for the selected category.
\n\n", - "SessionId": "@body('Summarize_Email_Intent')?['sessionId']" - }, "host": { "connection": { "name": "@parameters('$connections')['securitycopilot-1']['connectionId']" } }, "method": "post", - "path": "/process-prompt" - }, - "runAfter": { - "Check_if_domains_exist": [ - "Succeeded" - ] - }, - "type": "ApiConnection" + "body": { + "PromptContent": "/AskGpt\n### Task: Analyze the email for potential phishing indicators, focusing on the attachment's role and any actions it encourages. Output in JSON format.\n\n#### Pertinent Analysis Output from Previous Prompts:\n\"\"\"\nEmail intent: \n\n@{body('Summarize_Email_Intent')?['EvaluationResultContent']}\n\nAttachment data:\n\n@{body('Process_JSON')?['email_content']?['attachments']?[0]?['attachment_name']}\n@{body('Copilot_check_file_hashes')?['EvaluationResultContent']}\n\n\"\"\"\n\n### Task: Conduct a detailed analysis of the email attachment and its relationship to previously detected phishing indicators in the email body. Focus on the attachment’s alignment with the email's purpose and intent, especially looking for unusual characteristics that could indicate malicious intent.\n\nEmail Body Analysis Summary (from previous analysis):\n- Summary: The email body contains specific indicators of phishing intent, such as psychological manipulation, suspicious requests, or tactics encouraging action. Positive or neutral traits should not be included in the phishing indicators list.\n\n- Detected Indicators:\n - **Phishing Indicators**: Only include clear indicators of deception, focusing on psychological manipulation, suspicious requests, domain mismatches, urgency tactics, or unusual links/attachments.\n - **Positive Indicators (Optional)**: List any indicators of legitimacy if relevant, such as verified sender domains or expected file formats, but do not include these in phishing indicators.\n\n### Analysis Steps:\n\n1. **Email Body Context Recap**: Briefly summarize the key phishing indicators found in the email body to maintain context, emphasizing any manipulative tactics or suspicious requests encouraging user action.\n\n2. **Attachment Review**:\n - **Attachment Summary**: Describe the attachment’s content or file type (e.g., PDF invoice, Word document).\n - **Legitimacy and Intent Check**: Evaluate if the attachment logically supports the email’s purpose and intent. Consider if the attachment type, format, and file name are expected for legitimate communications from the sender.\n - **Potential Malicious Indicators**: Identify suspicious characteristics (e.g., executable file types, unexpected encryption, or file sizes) that suggest possible malicious intent.\n\n3. **Overall Phishing Likelihood Assessment**: Based on phishing indicators in the email body and attachment analysis, rate the phishing likelihood as 'High,' 'Medium,' 'Low,' or 'Inconclusive' if there’s insufficient information.\n\n### Output Format:\nProvide the output in JSON format as follows:\n\n{\n \"AttachmentReview\": {\n \"Findings\": \"Brief summary of attachment analysis or null if no findings.\",\n \"LegitimacyCheck\": \"true if the attachment logically aligns with the email’s intent, false if it does not, or null if no attachment.\"\n },\n \"EmailBodyAnalysis\": {\n \"EmailPurposeSummary\": \"One-sentence summary of the email's stated purpose.\",\n \"IntentSummary\": \"One-sentence summary of the email’s main intent, especially if it seeks user action.\",\n \"PhishingIndicators\": [\n \"List specific phishing indicators, focusing on manipulative tactics, suspicious requests, links, or attachment-related risks.\"\n ],\n \"PositiveIndicators\": [\n \"Optional: List any indicators of legitimacy here if relevant, such as verified sender domains or legitimate urgency.\"\n ],\n \"OverallPhishingLikelihood\": \"High, Medium, Low, or Inconclusive.\"\n }\n}\n\n### Example JSON Output:\n{\n \"AttachmentReview\": {\n \"Findings\": \"Attachment is labeled as an invoice but has a .exe file format, which is unusual.\",\n \"LegitimacyCheck\": false\n },\n \"EmailBodyAnalysis\": {\n \"EmailPurposeSummary\": \"Invoice notification with a call to action to view payment details.\",\n \"IntentSummary\": \"Encourages recipient to open a suspicious attachment and view payment instructions.\",\n \"PhishingIndicators\": [\n \"Urgency with payment warning.\",\n \"Suspicious file type in attachment.\",\n \"Request for sensitive payment details.\"\n ],\n \"PositiveIndicators\": [\n \"Recognized sender domain.\",\n \"Consistent company branding.\"\n ],\n \"OverallPhishingLikelihood\": \"High\"\n }\n}\n\n### Notes:\n- **Phishing Indicators** should reflect only suspicious elements directly linked to potential phishing.\n- **Positive Indicators** can be used to list legitimate traits if relevant.\n- The **Overall Phishing Likelihood** should primarily reflect the presence and severity of phishing indicators, with positive indicators reducing the risk assessment.", + "SessionId": "@body('Summarize_Email_Intent')?['sessionId']" + }, + "path": "/process-prompt", + "retryPolicy": { + "type": "fixed", + "count": 12, + "interval": "PT5M" + } + } }, "For_each_attachment": { + "foreach": "@triggerBody()?['attachments']", "actions": { "Check_if_binary_msg_eml_attachment": { "actions": { "Decode_Base64_ContentBytes": { - "inputs": "@base64ToBinary(items('For_each_attachment')?['contentBytes'])", - "type": "Compose" + "type": "Compose", + "inputs": "@base64ToBinary(item()?['contentBytes'])" }, "Set_variable_raw_email_msg_binary_decoded": { + "runAfter": { + "Show_bytes": [ + "Succeeded" + ] + }, + "type": "SetVariable", "inputs": { "name": "raw_email", - "value": "@outputs('Decode_Base64_ContentBytes')" - }, + "value": "@string(outputs('Show_bytes'))" + } + }, + "Show_bytes": { "runAfter": { "Decode_Base64_ContentBytes": [ "Succeeded" ] }, - "type": "SetVariable" + "type": "Compose", + "inputs": "@outputs('Decode_Base64_ContentBytes')" } }, + "runAfter": { + "Show_attachment": [ + "Succeeded" + ] + }, "else": { "actions": {} }, @@ -220,28 +281,31 @@ "or": [ { "equals": [ - "@item()?['contentType']", - "application/vnd.ms-outlook" + "@outputs('Show_attachment')?['contentType']", + "message/rfc822" ] }, { "equals": [ - "@item()?['contentType']", + "@outputs('Show_attachment')?['contentType']", "application/octet-stream" ] }, { "equals": [ - "@item()?['contentType']", - "message/rfc822" + "@outputs('Show_attachment')?['contentType']", + "application/vnd.ms-outlook" ] } ] }, "type": "If" + }, + "Show_attachment": { + "type": "Compose", + "inputs": "@item()" } }, - "foreach": "@triggerBody()?['attachments']", "runAfter": { "Set_variable_raw_email": [ "Succeeded" @@ -249,26 +313,13 @@ }, "type": "Foreach" }, - "Initialize_variable_attachments": { - "inputs": { - "variables": [ - { - "name": "attachments", - "type": "array", - "value": [ - "@body('Process_JSON')?['email_content']?['attachments']" - ] - } - ] - }, + "Initialize_variable_domains": { "runAfter": { - "Summarize_Email_Intent": [ + "Check_attachments_on_MDTI": [ "Succeeded" ] }, - "type": "InitializeVariable" - }, - "Initialize_variable_domains": { + "type": "InitializeVariable", "inputs": { "variables": [ { @@ -279,80 +330,32 @@ ] } ] - }, + } + }, + "Initialize_variable_email_body": { "runAfter": { - "Check_if_attachments_exist": [ + "Show_email_body_and_attachment_extracted_text_here": [ "Succeeded" ] }, - "type": "InitializeVariable" - }, - "Initialize_variable_email_body": { + "type": "InitializeVariable", "inputs": { "variables": [ { "name": "email_body", "type": "string", - "value": "@body('Process_JSON')?['email_content']?['body']" + "value": "@outputs('Show_email_body_and_attachment_extracted_text_here')" } ] - }, - "runAfter": { - "Process_JSON": [ - "Succeeded" - ] - }, - "type": "InitializeVariable" - }, - "Parse_Email": { - "inputs": { - "body": "@variables('raw_email')", - "function": { - "id": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('FunctionAppRG'), '/providers/Microsoft.Web/sites/', parameters('FunctionAppName'), '/functions/', variables('FunctionName01'))]" - } - }, - "runAfter": { - "For_each_attachment": [ - "Succeeded" - ] - }, - "type": "Function" + } }, - "Parse_JSON": { - "inputs": { - "content": "@body('Finalize_and_score')", - "schema": { - "properties": { - "evaluationResultContent": { - "type": "string" - }, - "evaluationResultType": { - "type": "string" - }, - "promptContent": { - "type": "string" - }, - "sessionId": { - "type": "string" - }, - "skillName": { - "type": "string" - }, - "skillSources": { - "type": "array" - } - }, - "type": "object" - } - }, + "Process_JSON": { "runAfter": { - "Finalize_and_score": [ + "Parse_Email": [ "Succeeded" ] }, - "type": "ParseJson" - }, - "Process_JSON": { + "type": "ParseJson", "inputs": { "content": "@body('Parse_Email')", "schema": { @@ -427,81 +430,573 @@ }, "type": "object" } + } + }, + "Set_variable_raw_email": { + "runAfter": { + "Export_email_(V2)": [ + "Succeeded" + ] }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "raw_email", + "type": "string", + "value": "@{body('Export_email_(V2)')}" + } + ] + } + }, + "Initialize_variable_raw_urls": { "runAfter": { - "Parse_Email": [ + "If_pdf_attachment_the_extract_text": [ "Succeeded" ] }, - "type": "ParseJson" + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "raw_urls", + "type": "array", + "value": "@body('Process_JSON')?['urls']" + } + ] + } }, - "Send_an_email_(V2)": { + "Initialize_variable_urls": { + "runAfter": { + "Initialize_variable_raw_urls": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", "inputs": { - "body": { - "Attachments": [ - { - "ContentBytes": "@base64(variables('raw_email'))", - "Name": "@concat(body('Process_JSON')?['email_content']?['subject'], '.eml')" + "variables": [ + { + "name": "urls", + "type": "array", + "value": [] + } + ] + } + }, + "For_each_raw_url_truncate_if_too_long": { + "foreach": "@variables('raw_urls')", + "actions": { + "If_URL_length_greater_than_100": { + "actions": { + "Append_to_array_variable": { + "type": "AppendToArrayVariable", + "inputs": { + "name": "urls", + "value": "@concat(substring(string(item()), 0, 100), '...')" + } } - ], - "Body": "@{outputs('Show_HTML_report_here')}
@{body('Generate_HTML_report_from_JSON')}
", + "path": "/html2text" + } + }, + "Alert_-_Get_incident_from_systemalertid": { + "runAfter": { + "If_empty_results_wait_15_minutes_for_logs_to_show_up": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "get", + "path": "[concat('/Incidents/subscriptions/@{encodeURIComponent(''', parameters('SubscriptionId'), ''')}/resourceGroups/@{encodeURIComponent(''', parameters('LogAnalyticsResourceGroup'),''')}/workspaces/@{encodeURIComponent(''', parameters('LogAnalyticsWorkspId') ,''')}/alerts/@{encodeURIComponent(variables(''systemalertid_query_results'')[0][''SystemAlertId''])}')]" + } + }, + "Add_comment_to_incident_(V3)_armid": { + "runAfter": { + "Alert_-_Get_incident_from_systemalertid": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "post", + "body": { + "incidentArmId": "@body('Alert_-_Get_incident_from_systemalertid')?['id']", + "message": "********** Phishing Analysis Generated by Security Copilot **********
@{body('Html_to_text')}
---
Report generated: @{utcNow()}
Generated with AI, check for accuracy.
@{body('Generate_HTML_report_from_JSON')}
***********
Incident URL:
@{body('Alert_-_Get_incident_from_systemalertid')?['properties']?['incidentUrl']}
Generated with AI, check for accuracy.
***********
", + "Importance": "Normal" + }, + "path": "/v2/SharedMailbox/Mail" + } + }, + "If_empty_results_wait_15_minutes_for_logs_to_show_up": { + "actions": { + "Delay_15_min": { + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Minute" + } + } + }, + "Second_try_set_variable_systemalertid_query_results": { + "runAfter": { + "Query_to_get_systemalertid_second_try": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "systemalertid_query_results", + "value": "@body('Query_to_get_systemalertid_second_try')?['value']" + } + }, + "Query_to_get_systemalertid_second_try": { + "runAfter": { + "Delay_15_min": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azuremonitorlogs']['connectionId']" + } + }, + "method": "post", + "body": "SecurityAlert\n| where isnotempty(Entities)\n| extend MailEntities = parse_json(Entities)\n| mv-expand MailEntities\n| where MailEntities.Type == \"mailMessage\"\n| extend NetworkMessageId = tostring(MailEntities.NetworkMessageId)\n| where NetworkMessageId == \"@{variables('network_message_id')}\"\n| distinct SystemAlertId", + "path": "/queryData", + "queries": { + "subscriptions": "[parameters('SubscriptionId')]", + "resourcegroups": "[parameters('LogAnalyticsResourceGroup')]", + "resourcetype": "Log Analytics Workspace", + "resourcename": "[parameters('LogAnalyticsWorkspaceName')]", + "timerange": "14d" + } + } + } + }, + "runAfter": { + "Set_variable_systemalertid_query_results": [ + "Succeeded" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "less": [ + "@length(body('Query_to_get_systemalertid')?['value'])", + 1 + ] + } + ] + }, + "type": "If" + }, + "Set_variable_systemalertid_query_results": { + "runAfter": { + "Query_to_get_systemalertid": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "systemalertid_query_results", + "value": "@body('Query_to_get_systemalertid')?['value']" + } + }, + "Query_to_get_systemalertid": { + "runAfter": { + "Html_to_text": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azuremonitorlogs']['connectionId']" + } + }, + "method": "post", + "body": "SecurityAlert\n| where isnotempty(Entities)\n| extend MailEntities = parse_json(Entities)\n| mv-expand MailEntities\n| where MailEntities.Type == \"mailMessage\"\n| extend NetworkMessageId = tostring(MailEntities.NetworkMessageId)\n| where NetworkMessageId == \"@{variables('network_message_id')}\"\n| distinct SystemAlertId", + "path": "/queryData", + "queries": { + "subscriptions": "[parameters('SubscriptionId')]", + "resourcegroups": "[parameters('LogAnalyticsResourceGroup')]", + "resourcetype": "Log Analytics Workspace", + "resourcename": "[parameters('LogAnalyticsWorkspaceName')]", + "timerange": "14d" + } + } + } + }, + "runAfter": { + "Initialize_systemalert_id_query_results": [ + "Succeeded" + ] + }, + "else": { + "actions": { + "Send_an_email_from_a_shared_mailbox_(V2)_without_incident_url": { + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['office365-1']['connectionId']" + } + }, + "method": "post", + "body": { + "MailboxAddress": "[parameters('SharedMailboxAddress')]", + "To": "[parameters('HTMLReportRecipient')]", + "Subject": "Phishing Email Analysis", + "Body": "
@{body('Generate_HTML_report_from_JSON')}
***********
Generated with AI, check for accuracy.
***********
", + "Attachments": [ + { + "Name": "original_email.eml", + "ContentBytes": "@base64(body('Export_email_(V2)'))" + } + ], + "Importance": "Normal" + }, + "path": "/v2/SharedMailbox/Mail" + } + } + } + }, + "expression": { + "and": [ + { + "equals": [ + "@variables('is_defender_incident')", + true + ] + } + ] + }, + "type": "If" + }, + "Parse_Email": { + "runAfter": { + "For_each_raw_email_attached_to_carrier_email": [ + "Succeeded" + ] + }, + "type": "Function", + "inputs": { + "body": "@variables('raw_email')", + "function": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/resourceGroups/', parameters('FunctionAppResourceGroup'), '/providers/Microsoft.Web/sites/', parameters('FunctionAppName'), '/functions/', variables('FunctionName01'))]" + } + } + }, + "Normalize_JSON": { + "runAfter": { + "Security_Copilot_Email_Analysis": [ + "Succeeded" + ] + }, + "type": "Function", + "inputs": { + "body": "@body('Security_Copilot_Email_Analysis')?['EvaluationResultContent']", + "function": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/resourceGroups/', parameters('FunctionAppResourceGroup'), '/providers/Microsoft.Web/sites/', parameters('FunctionAppName'), '/functions/', variables('FunctionName02'))]" + } + } + }, + "Generate_HTML_report_from_JSON": { + "runAfter": { + "Parse_normalized_JSON": [ + "Succeeded" + ] + }, + "type": "Function", + "inputs": { + "body": "@body('Parse_normalized_JSON')", + "function": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/resourceGroups/', parameters('FunctionAppResourceGroup'), '/providers/Microsoft.Web/sites/', parameters('FunctionAppName'), '/functions/', variables('FunctionName03'))]" + } + } + } + }, + "outputs": {} + }, + "parameters": { + "$connections": { + "value": { + "office365-1": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/office365')]", + "connectionId": "[resourceId('Microsoft.Web/connections', variables('Office365-ConnectionName'))]", + "connectionName": "[variables('Office365-ConnectionName')]" + }, + + "securitycopilot-1": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/securitycopilot')]", + "connectionId": "[resourceId('Microsoft.Web/connections', variables('Securitycopilot-ConnectionName'))]", + "connectionName": "[variables('Securitycopilot-ConnectionName')]" + }, + + "azuresentinel": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azuresentinel')]", + "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureSentinel-ConnectionName'))]", + "connectionName": "[variables('AzureSentinel-ConnectionName')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + } + }, + "azuremonitorlogs": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azuremonitorlogs')]", + "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureMonitorLogs-ConnectionName'))]", + "connectionName": "[variables('AzureMonitorLogs-ConnectionName')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + } + }, + "conversionservice-1": { + "id": "[concat('/subscriptions/', parameters('SubscriptionId'), '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/conversionservice')]", + "connectionId": "[resourceId('Microsoft.Web/connections', variables('ConversionService-ConnectionName'))]", + "connectionName": "[variables('ConversionService-ConnectionName')]" + } + } + } + } + } + } + ] +} \ No newline at end of file diff --git a/Logic Apps/SecCopilot-UserReportedPhishing/DefenderKqlPlugins_automation.yaml b/Logic Apps/SecCopilot-UserReportedPhishing/DefenderKqlPlugins_automation.yaml index 6446d4ea..d4d5ec68 100644 --- a/Logic Apps/SecCopilot-UserReportedPhishing/DefenderKqlPlugins_automation.yaml +++ b/Logic Apps/SecCopilot-UserReportedPhishing/DefenderKqlPlugins_automation.yaml @@ -13,8 +13,8 @@ SkillGroups: Description: The subject of the email Required: true Settings: - Target: Defender - Template: |- + Target: Defender + Template: |- EmailEvents | where Subject == '{{subject}}' - Name: GetUrlClicksBySubjectAndSenderEmail @@ -28,8 +28,8 @@ SkillGroups: Description: The sender of the email Required: true Settings: - Target: Defender - Template: |- + Target: Defender + Template: |- let email = EmailEvents | where Subject == '{{subject}}' and SenderFromAddress == '{{senderEmail}}' | project NetworkMessageId; diff --git a/Logic Apps/SecCopilot-UserReportedPhishing/readme.md b/Logic Apps/SecCopilot-UserReportedPhishing/readme.md index 90340b8d..aedd6e8b 100644 --- a/Logic Apps/SecCopilot-UserReportedPhishing/readme.md +++ b/Logic Apps/SecCopilot-UserReportedPhishing/readme.md @@ -39,7 +39,7 @@ To deploy the above logic app, you need to
+- Choose Client Certificate Auth or Oauth (using user account that can submit prompts) to create connection with Microsoft Security Copilot logic app connector.
+
+
+- Important: the account selected for the authentication to Security Copilot must have the Contributor role on Security Copilot and reader privileges on Defender, Sentinel, Entra and Intune.
+- Similarly, authenticate the API connection to Exchange Online (the correct setup of connection is needed even if the Logic App is configured to not send emails)
+- Assign the role of 'Microsoft Sentinel Responder' to the Managed Identity of the Logic App within the Resource Group that contains the Sentinel's Log Analytics workspace.
+- Set the values for the Logic App parameters
+
+- Automate Sentinel incident investigation by adding the Logic App to Microsoft Sentinel automation rule. Alternatively, this logic app can be called on-demand by right-clicking on a Sentinel incident, choose **Run Playbook** option.
+
+## Microsoft Security Copilot skills
+- Following built-in skills are being used to perform investigation on a given incident entity type
+
+| Incident entity | Security Copilot skill |
+| --------------- | -------------------------- |
+| Process name / commandline | ProcessAnalyzer |
+| Account name | GetEntraUserDetails |
+| Account names | GetEntraRiskyUsers |
+| Device name | GetIntuneDevices |
+| IP address | AbuseIPDB |
+| Indicator of Compromise (IoC) | GetReputationsForIndicators |
+
+
+## Incident Investigation Template
+- Automated incident investigation summary is structured in following order. Modify the prompt to re-order or change the investigation template.
+ - Incident Overview,
+ - Incident Description,
+ - IP Addresses Analysis,
+ - Account Analysis,
+ - Users Risk Analysis,
+ - Devices Analysis,
+ - Processes Analysis,
+ - Indicators Analysis from MDTI,
+ - Possible mitigation steps and Conclusion
+
+
diff --git a/Logic Apps/ThreatBulletinCopilot/readme.md b/Logic Apps/ThreatBulletinCopilot/readme.md
index 33a274f2..ccaf8929 100644
--- a/Logic Apps/ThreatBulletinCopilot/readme.md
+++ b/Logic Apps/ThreatBulletinCopilot/readme.md
@@ -1,4 +1,4 @@
-# Copilot-For-Security-ThreatBulletin
+# Security Copilot-ThreatBulletin
Author: Sean Wasonga
## Overview
This playbook serves as a cornerstone for Security Copilot, facilitating integration with Microsoft Defender Threat Intelligence (MDTI) to swiftly provide the latest threat bulletins for threat articles. By consolidating this connection, users can efficiently access comprehensive threat article information. Over time, this foundational playbook may expand to incorporate additional capabilities.
@@ -13,7 +13,7 @@ Prior to beginning the installation process, it's crucial to confirm that you ha
## Deployment
-
+
|
|
| SkillName | +Description | +Parameters | +
|---|---|---|
| SecurityCopilotLogins | +Fetches Security Copilot Standalone Portal logins in the last 3 days | +
+
|
+
+| SkillName | +Description | +Parameters | +
|---|---|---|
| GetDefenderDevices | +Get the top 10 devices from defender based on device state, exposure for the last 8 hours | +
+
|
+
| GetLatestEmailsByRecipient | +Fetches the latest emails received by the user with the specified email address | +
+
|
+
+
+| SkillName | +Description | +Parameters | +
|---|---|---|
| SentinelIncidentComments | +Fetch Sentinel incident comments | +
+
|
+
+| SkillName | +Description | +Parameters | +
|---|---|---|
| GetTrustedUsers | +Fetches the list of trusted users that is being maintained as a Microsoft Sentinel watchlist | +
+
|
+
+Example Prompt: Create an eDiscovery case in Purview with the Case name "Test-123", response should be a bulleted list.
+Inputs: [Case name]
+Example Prompt: Add a custodian to the eDiscovery case in Purview, use the Case ID "1xxxx234-XXXX-XXXX-a1cd-fxxxxxxxxxx0" and add this email "test@test123.com".
+Inputs: [Email], [Case ID]
+Example Prompt: Apply hold on eDiscovery custodian using the Custodian ID "2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx6".
+Inputs: [Custodian ID]
+Example Prompt: Add new userSource object associated with Custodian ID "2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx6", include custodian email "test@test123.com" and specify sources as Mailbox, Site.
+Inputs: [Custodian ID], [Email]
+Example Prompt: Add a new eDiscovery Search object with Case ID "1xxxx234-XXXX-XXXX-a1cd-fxxxxxxxxxx0" with the display name "Test-123-search", and add dataSourceScopes to allTenantMailboxes, allTenantSites, allCaseCustodians.
+Inputs: [Case ID], [Display Name]
+Example Prompt: Run an estimate of the number of emails and documents in eDiscovery with Search ID "2xxxxxxx-xxx4-xxxd-4xxx-5xxxxxxxxxxx".
+Inputs: [Search ID]
+Example Prompt: Create a new eDiscovery ReviewSet for Case ID "1xxxx234-XXXX-XXXX-a1cd-fxxxxxxxxxx0" with Display Name "Test-123-reviewset".
+Inputs: [Case ID]
+Example Prompt: Use Case ID "1xxxx234-XXXX-XXXX-a1cd-fxxxxxxxxxx0" and ReviewSet ID "dxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" to initiate an export.
+Inputs: [Case ID], [Review Set ID]
+Example Prompt: Initiate an export from the Search with Case ID "1xxxx234-XXXX-XXXX-a1cd-fxxxxxxxxxx0" and Search ID "dxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx". Additional parameters: displayName="Export 1 - simple PST-V2", exportCriteria="searchHits", additionalOptions="none", exportFormat="pst".
+Inputs: [Case ID], [Search ID]
+Example Prompt: Use the Case ID from above or use this Case ID "1xxxx234-XXXX-XXXX-a1cd-fxxxxxxxxxx0", and update index on Custodian.
+Inputs: [Case ID]
+Example Prompt: Use the Case ID from above or use this Case ID "1xxxx234-XXXX-XXXX-a1cd-fxxxxxxxxxx0", get a list of the caseOperation objects and their properties.
+Inputs: [Case ID]
+| SkillName | +Description | +Parameters | +
|---|---|---|
| RedactPII | +Redacts Personally Identifiable Information (PII) from the provided text | +
+
|
+
+- GET /simple/<data> - Will return a JSON contains <data> and few other fields " - text += "
- GET /params/<data> - Will return a JSON contains <data> and few other fields passed in URL Params" - text += "
- GET /params_auth_header/<data> - Will return a JSON contains <data> and few other fields passed in URL Params, along with Authorization field passed in header" - text += "
- GET /params_auth_data/<data> - Will return a JSON contains <data> and few other fields passed in URL Params, along with Authorization field passed in data" - text += "
- POST /json/<data> - Will return a JSON contains <data> and few other fields passed in payload JSON"
- return text
-
-# ---------------- GET methods
-@app.route('/simple/', methods=['GET'])
-def get_simple(data):
- obj = ReflectorSimple(data,getIpAddr(request),request.user_agent.string)
- response = jsonify(obj.getDict())
- return response
-
-@app.route('/simple/multiple/', methods=['GET'])
-def get_simple_multiple(data):
- count = int(data)
- objs = []
- for i in range(0,count):
- obj = ReflectorSimple("Data_"+str(i),getIpAddr(request),request.user_agent.string)
- objs.append(obj.getDict())
- response = jsonify(objs)
- return response
-
-# This method accepts query parameters,passes them to create a ReflectorJson JSON object
-@app.route('/params/', methods=['GET'])
-def get_params_data(data):
- args = request.args
- jsonData = args
- obj = ReflectorJson(data,jsonData,request.remote_addr,request.user_agent.string)
- response = jsonify(obj.getDict())
- return response
-
-@app.route('/params_auth_header/', methods=['GET'])
-def get_params_auth_header(data):
- args = request.args
- jsonData = args
- # Extract the Authorization Value from the Header
- authToken = "via Header:" + request.headers["Authorization"]
- obj = ReflectorJsonAuth(data,authToken,jsonData,request.remote_addr,request.user_agent.string)
- response = jsonify(obj.getDict())
- return response
-
-@app.route('/params_auth_data/', methods=['GET'])
-def get_params_auth_data(data):
- args = request.args
- jsonData = args
- # Extract the Authorization Value from the Parameter
- authToken = args["Authorization"]
- obj = ReflectorJsonAuth(data,authToken,jsonData,request.remote_addr,request.user_agent.string)
- response = jsonify(obj.getDict())
- return response
-
-# ---------------- POST methods
-@app.route('/json/', methods=['POST'])
-def get_json(data):
- jsonData = ""
- if(request.is_json):
- try:
- # Handle Error
- jsonData = request.get_json(force=True) #request.json
- print("Received JSON:",jsonData)
- except Exception as e:
- print("Error",e)
- print("Request Content", str(jsonData))
- result = {'Error': "'" + str(e) + "'"}
- return result, 400 # Return error as HTTP 400
- obj = ReflectorJson(data,jsonData,request.remote_addr,request.user_agent.string)
- response = jsonify(obj.getDict())
- return response
-
-
-#---- OPTIONAL: Use this URL to return YAML files if you do not want to the host the files in FTP/Azure/Blob
-#---- NOTE: All files should be placed in the ./Files folder
-@app.route('/file/
+`https://purview.microsoft.com/datalossprevention/alertspage/fullpage?tid=0527ecb7-06fb-4769-b324-fd4a3bb865eb&alertsviewid=overview&id=da7618d0-35a6-71b8-3400-08dd5766cd5a`
+
+To create a link, I simply need to ensure that the data required to support a given URL format is present in the output or provided some other way. In this case, the output contains a list of DLP alerts along with their associated alert ID. Since my Tenant ID is missing, I'll need to get it. There are multiple methods for getting this, but the easiest is to simply view the [Tenant Properties within the Azure Portal](https://portal.azure.com/#view/Microsoft_AAD_IAM/TenantProperties.ReactView).
+
+Now that I have the Tenant ID, I can hardcode that part of the URL. From my experience working in Defender XDR and Purview, I know that alert IDs associated with Purview include a preceding 'dl' that I'll need to remove if I want to view these alerts from the Purview portal.
+
+To generate this URL, I will again use the **/AskGPT** skill and prompt Security Copilot to:
+- Create a link for the alerts.
+- Provide it with the root URL that contains my Tenant ID.
+- Ask it to remove the first two characters of the alert ID.
+
+⚠️ Please read next prompt before executing and don't forget to insert your Tenant ID in the URL before pasting.
+
+```
+/AskGPT For each user include a direct link to the alert in Microsoft Purview portal using the following URL format:
+Purview: [{Alert Title}](https://purview.microsoft.com/datalossprevention/alertspage/fullpage?tid={Replace-with-your-tenant-ID-and-remove-curly-brackets}&alertsviewid=overview&id={alertId with the first two 'dl' characters of the AlertID removed})
+```
+
+⚠️ If I'd prefer working out of Defender XDR instead of Purview, I can leave the preceding 'dl' characters in the Alert ID and use the Defender XDR URL `https://security.microsoft.com/alerts/{alert ID}`, modifying the prompt accordingly. To avoid confusing the LLM, I'll choose either Purview or Defender XDR before pasting the prompt.
+
+```
+/AskGPT For each user include a direct link to the alert in Defender XDR portal using the following URL format:
+[{Alert Title}](https://security.microsoft.com/alerts/{alert Id})
+```
+
+If the above prompt failed, verify that you updated the Tenant ID.
+
+
+
+
+### Ensuring consistent formatting
+
+While I value the friendly, conversational, and non-repetitive nature that Generative AI provides, I need the output to be consistently formatted each time. To achieve this, I'll provide clear instructions that integrate my previous prompts, URL modifications, and additional format guidelines.
+
+Since large language models (LLMs) work best when using specific delimiters, and Markdown is a well-established language for such delimiters, I will combine both the [Security Copilot prompt engineering best practices](https://learn.microsoft.com/en-us/copilot/security/prompting-tips) and the [general best practices for effective prompting of LLMs](#general-best-practices-for-effective-prompting-llms) outlined at the top of this page. By leveraging both of these resources, I’ve not only harnessed the power of Security Copilot, but I will also ensure that the reasoning output is consistently formatted each time I run this prompt.
+
+```
+Using the Purview plugin, get the last 10 DLP alerts with a severity of high. Assume the role of an expert **Data Loss Prevention (DLP) engineer** tasked with generating a **professional, manager-focused summary** of a DLP triage workflow for DLP alerts.
+
+- For **each user**, write a **concise, actionable summary** addressed to their manager.
+- Begin the summary with the user's **User Principal Name (UPN), Job Title, and Department** to establish context.
+- Highlight **significant trends, behaviors, or issues** observed in their **DLP alerts** using **chain-of-thought reasoning** to identify patterns, similarities, or contributing factors across the alerts.
+- Provide **clear, actionable recommendations** to mitigate risk, prioritize incidents, and improve compliance.
+- Include a direct **link to the alert in Microsoft Purview** using the following URL format: [{Alert Title}](https://purview.microsoft.com/datalossprevention/alertspage/fullpage?tid=0527ecb7-06fb-4769-b324-fd4a3bb865eb&alertsviewid=overview&id={alert Id}
+- Note that if the alert ID begins with 'dl' this portion of the alertId should be removed before creating the URL. Example: https://purview.microsoft.com/datalossprevention/alertspage/fullpage?tid=0527ecb7-06fb-4769-b324-fd4a3bb865eb&alertsviewid=overview&id=dlda7618d0-35a6-71b8-9200-08dd5767802a
+...becomes...
+https://purview.microsoft.com/datalossprevention/alertspage/fullpage?tid=0527ecb7-06fb-4769-b324-fd4a3bb865eb&alertsviewid=overview&id=da7618d0-35a6-71b8-9200-08dd5767802a
+
+**Tone and Style:** Write in clear, professional, and manager-focused language, emphasizing risk, mitigation strategies, and actionable insights.
+
+## Output format ##
+**User**: {User Principal Name (UPN)}
+
+**Summary:**
+{Concise, actionable summary of the DLP alerts for the user, highlighting significant trends, behaviors, or issues observed in their DLP alerts using chain-of-thought reasoning to identify patterns, similarities, or contributing factors across the alerts. Summary should be under 150 words.}
+
+**Recommendations:**
+1. **Recommendation category**: {Any recommendation that supports mitigate risk, prioritize incidents, and improve compliance}
+2. **Recommendation category**: {Any additional recommendations that supports mitigate risk, prioritize incidents, and improve compliance}
+3. etc...
+
+**Alert Links:**
+- [Alert Title](Alert Link)
+- [Alert Title](Alert Link)
+---
+```
+
+
+
+---
+
+### Leveraging Promptbooks
+
+To make repeating the execution of this prompt easier, I'll create a promptbook. Promptbooks can store multiple prompts and support workflows that allow users to easily rerun one or more prompts at a later time. For advanced prompts like this that include multiple lines of instructions and formatting requirements, using a promptbook reduces the likelihood that I'll incur errors from typing, saves me time from having to manually copy and paste prompts from an external library, and allows me to share it with others within my organization.
+
+To create a promptbook from within a session, I simply need to click the checkbox on the top left corner of the prompt, and select the "Create promptbook" icon at the top. However, it's important to note that promptbooks use angle brackets`< >` to denote variables. As such, the existence of them must only be used for variables without spaces.
+
+
+
+Next, I'll provide a name, relevant tags (hit ENTER after each tag), and a description. After this, I'll hover my mouse over the prompt box and click the pencil icon to edit the prompt.
+
+
+
+Since I don't always want "10" alerts exactly, I'll make this a variable by replacing the number "10" with `