Saltar a contenido

Microsoft Sentinel Cheatsheet

Microsoft Sentinel is a cloud-native Security Information and Event Management (SIEM) and Security Orchestration, Automation, and Response (SOAR) solution that provides intelligent security analytics and Inteligencia de Amenazas across the enterprise. Built on Azure, Sentinel delivers scalable security monitoring, advanced threat detection, and automated respuesta a incidentes capabilities that enable security teams to detect, investigate, and respond to threats at cloud scale.

Platform Overview

Core Architecture

Microsoft Sentinel operates as a fully managed servicio within the Azure ecosystem, leveraging Azure Monitor Logs as its underlying data platform. The architecture consists of several integrated components that work together to provide comprehensive security operations capabilities.

The data ingestion layer suppuertos a wide variety of data sources through native connectors, REST APIs, and custom integrations. Sentinel can collect data from Microsoft servicios (Office 365, Azure AD, Windows Security Events), third-party security tools, cloud platforms, network devices, and custom applications.

The analytics engine utilizes machine learning algoritmos, behavioral analytics, and rule-based detection to identify potential security threats. Sentinel's detection capabilities include built-in analytics rules, custom KQL (Kusto Query Language) queries, machine learning models for anomaly detection, and Inteligencia de Amenazas integration.

clave Features

# Core Platform Capabilities
- Cloud-native SIEM and SOAR platform
- Advanced threat detection and analytics
- Security orchestration and automated response
- Inteligencia de Amenazas integration
- Machine learning and behavioral analytics
- Custom dashboards and workbooks
- Incident management and investigation
- caza de amenazas capabilities

Data Connectors and Ingestion

Microsoft servicio Connectors

# Azure Active Directory connector
Connect-AzAccount
Set-AzContext -SubscriptionId "<subscription-id>"

# Enable Azure AD connector via PowerShell
$resourceGroupName = "sentinel-rg"
$workspaceName = "sentinel-workspace"
$dataConnectorId = "AzureActiveDirectory"

New-AzSentinelDataConnector -ResourceGroupName $resourceGroupName -WorkspaceName $workspaceName -Id $dataConnectorId -Kind "AzureActiveDirectory"

# Office 365 connector configuración
$office365Config = @\\\\{
    tenantId = "<tenant-id>"
    dataTypes = @\\\\{
        exchange = @\\\\{ state = "Enabled" \\\\}
        sharePoint = @\\\\{ state = "Enabled" \\\\}
        teams = @\\\\{ state = "Enabled" \\\\}
    \\\\}
\\\\}

# Azure Security Center connector
$ascConfig = @\\\\{
    subscriptionId = "<subscription-id>"
    dataTypes = @\\\\{
        alerts = @\\\\{ state = "Enabled" \\\\}
        recommendations = @\\\\{ state = "Enabled" \\\\}
    \\\\}
\\\\}

Third-Party Connectors

# Syslog connector configuración
# Configure rsyslog on Linux systems
sudo nano /etc/rsyslog.d/95-omsagent.conf

# Add configuración for Sentinel
*.* @@<workspace-id>.ods.opinsights.azure.com:514

# Restart rsyslog servicio
sudo systemctl restart rsyslog

# CEF (Common Event Format) connector
# Configure log forwarding for CEF events
| logger -p local4.warn -t CEF "CEF:0 | Microsoft | ATA | 1.9.0.0 | AbnormalSensitiveGroupMembershipChangeSuspiciousActivity | Abnormal modification of sensitive groups | 5 | start=2018-12-12T18:52:58.0000000Z app=GroupMembershipChangeEvent suser=krbtgt msg=krbtgt has been added to the following sensitive groups: Domain Admins." |

# Custom REST API connector
curl -X POST "https://<workspace-id>.ods.opinsights.azure.com/api/logs?api-version=2016-04-01" \
  -H "autorización: Sharedclave <workspace-id>:<shared-clave>" \
  -H "Content-Type: application/json" \
  -H "Log-Type: CustomSecurityLog" \
  -d '\\\\{
    "timestamp": "2023-01-01T12:00:00Z",
    "severity": "High",
    "source_ip": "192.168.1.100",
    "event_type": "suspicious_login",
    "user": "admin@company.com"
  \\\\}'

Azure Monitor Agent configuración

# Install Azure Monitor Agent
$vmName = "security-vm"
$resourceGroupName = "security-rg"
$location = "East US"

# Create data collection rule
$dcrConfig = @\\\\{
    location = $location
    properties = @\\\\{
        dataSources = @\\\\{
            windowsEventLogs = @(
                @\\\\{
                    name = "SecurityEvents"
                    streams = @("Microsoft-SecurityEvent")
                    xPathQueries = @(
                        "Security!*[System[(EventID=4624 or EventID=4625 or EventID=4648)]]"
                    )
                \\\\}
            )
            syslog = @(
                @\\\\{
                    name = "SyslogEvents"
                    streams = @("Microsoft-Syslog")
                    facilityNames = @("auth", "authpriv", "demonio")
                    logLevels = @("Warning", "Error", "Critical")
                \\\\}
            )
        \\\\}
        destinations = @\\\\{
            logAnalytics = @(
                @\\\\{
                    workspaceResourceId = "/subscriptions/<subscription-id>/resourceGroups/<rg-name>/providers/Microsoft.OperationalInsights/workspaces/<workspace-name>"
                    name = "SentinelWorkspace"
                \\\\}
            )
        \\\\}
        dataFlows = @(
            @\\\\{
                streams = @("Microsoft-SecurityEvent")
                destinations = @("SentinelWorkspace")
            \\\\}
        )
    \\\\}
\\\\}

KQL (Kusto Query Language) for Security Analytics

Basic KQL sintaxis

// Simple table query
SecurityEvent
|take 10

// Time range filtering
SecurityEvent
|where TimeGenerated > ago(1h)

// Field filtering
SecurityEvent
|where EventID == 4624
|where Account contains "admin"

// Multiple conditions
SecurityEvent
|where TimeGenerated > ago(24h)
|where EventID in (4624, 4625, 4648)
|where Account !contains "SYSTEM"

// String operations
SecurityEvent
|where Activity contains "Logon"
|where Account startswith "admin"
|where Computer endswith ".contoso.com"

Advanced Analytics Queries

// Failed login analysis
SecurityEvent
|where EventID == 4625
|where TimeGenerated > ago(1h)
|summarize FailedAttempts = count() by Account, Computer, IpAddress
|where FailedAttempts > 5
|sort by FailedAttempts desc

// escalada de privilegios detection
SecurityEvent
|where EventID == 4672
|where TimeGenerated > ago(24h)
|where Subjectnombre de usuario !in ("SYSTEM", "LOCAL servicio", "NETWORK servicio")
|summarize ElevationCount = count() by Subjectnombre de usuario, Computer
|where ElevationCount > 10
|sort by ElevationCount desc

// movimiento lateral detection
SecurityEvent
|where EventID == 4624
|where LogonType in (3, 10)
|where TimeGenerated > ago(1h)
|summarize UniqueComputers = dcount(Computer) by Account
|where UniqueComputers > 5
|sort by UniqueComputers desc

// Anomalous proceso execution
SecurityEvent
|where EventID == 4688
|where TimeGenerated > ago(1h)
|where proceso contains "powershell.exe" or proceso contains "cmd.exe"
|where comandoLine contains "Invoke-" or comandoLine contains "Download"
|project TimeGenerated, Computer, Account, proceso, comandoLine
|sort by TimeGenerated desc

caza de amenazas Queries

// Suspicious PowerShell activity
SecurityEvent
|where EventID == 4688
|where proceso endswith "powershell.exe"
|where comandoLine has_any ("Invoke-Expression", "IEX", "Invoke-WebRequest", "DownloadString", "Encodedcomando")
|extend Decodedcomando = base64_decode_tostring(extract(@"-Encodedcomando\s+([A-Za-z0-9+/=]+)", 1, comandoLine))
|project TimeGenerated, Computer, Account, comandoLine, Decodedcomando
|sort by TimeGenerated desc

// Living off the land techniques
SecurityEvent
|where EventID == 4688
|where proceso has_any ("certutil.exe", "bitsadmin.exe", "regsvr32.exe", "rundll32.exe", "mshta.exe")
|where comandoLine has_any ("http", "ftp", "download", "urlcache", "split")
|project TimeGenerated, Computer, Account, proceso, comandoLine
|sort by TimeGenerated desc

// Credential dumping detection
SecurityEvent
|where EventID in (4656, 4663)
|where ObjectName has_any ("lsass.exe", "SAM", "SECURITY", "SYSTEM")
|where AccessMask in ("0x1010", "0x1400", "0x100000")
|summarize AccessAttempts = count() by Account, Computer, ObjectName
|where AccessAttempts > 3
|sort by AccessAttempts desc

// DNS tunneling detection
DnsEvents
|where TimeGenerated > ago(1h)
|where QueryType == "TXT" or QueryType == "NULL"
|where Name contains "." and strlen(Name) > 50
|summarize QueryCount = count() by ClientIP, Name
|where QueryCount > 10
|sort by QueryCount desc

Machine Learning Analytics

// User behavior analytics
SigninLogs
|where TimeGenerated > ago(30d)
|where ResultType == "0"
|summarize
    LoginCount = count(),
    UniqueIPs = dcount(IPAddress),
    UniqueLocations = dcount(Location),
    UniqueDevices = dcount(DeviceDetail.deviceId)
    by UserPrincipalName
|extend RiskScore = (UniqueIPs * 2) + (UniqueLocations * 3) + (UniqueDevices * 1)
|where RiskScore > 20
|sort by RiskScore desc

// Anomalous data transfer detection
CommonSecurityLog
|where TimeGenerated > ago(1h)
|where DeviceVendor == "Palo Alto Networks"
|where Activity == "TRAFFIC"
|summarize TotalBytes = sum(SentBytes + ReceivedBytes) by SourceIP, DestinationIP
|extend BytesInMB = TotalBytes / (1024 * 1024)
|where BytesInMB > 1000
|sort by BytesInMB desc

// Behavioral baseline deviation
SecurityEvent
|where TimeGenerated > ago(30d)
|where EventID == 4624
|where LogonType == 2
|summarize
    AvgLoginsPerDay = count() / 30,
    StdDev = stdev(todouble(1))
    by Account
|join kind=inner (
    SecurityEvent
|where TimeGenerated > ago(1d)
|where EventID == 4624
|where LogonType == 2
|summarize TodayLogins = count() by Account
) on Account
|extend Deviation = abs(TodayLogins - AvgLoginsPerDay) / StdDev
|where Deviation > 3
|sort by Deviation desc

Analytics Rules and Detection

Scheduled Analytics Rules

// Create scheduled rule for fuerza bruta detection
let threshold = 10;
SecurityEvent
|where TimeGenerated > ago(1h)
|where EventID == 4625
|summarize FailedAttempts = count() by Account, IpAddress, Computer
|where FailedAttempts >= threshold
|extend
    Severity = case(
        FailedAttempts >= 50, "High",
        FailedAttempts >= 25, "Medium",
        "Low"
    ),
    Tactics = "CredentialAccess",
    Techniques = "T1110"
|project Account, IpAddress, Computer, FailedAttempts, Severity, Tactics, Techniques

// Suspicious file creation rule
SecurityEvent
|where TimeGenerated > ago(1h)
|where EventID == 4663
|where ObjectName endswith ".exe" or ObjectName endswith ".dll" or ObjectName endswith ".bat"
|where ObjectName has_any ("temp", "appdata", "programdata")
|where AccessMask == "0x2"
|extend
    Severity = "Medium",
    Tactics = "Execution",
    Techniques = "T1059"
|project TimeGenerated, Computer, Account, ObjectName, Severity, Tactics, Techniques

// escalada de privilegios rule
SecurityEvent
|where TimeGenerated > ago(1h)
|where EventID == 4672
|where Subjectnombre de usuario !in ("SYSTEM", "LOCAL servicio", "NETWORK servicio")
|where PrivilegeList has_any ("SeDebugPrivilege", "SeTakeOwnershipPrivilege", "SeLoadDriverPrivilege")
|extend
    Severity = "High",
    Tactics = "PrivilegeEscalation",
    Techniques = "T1134"
|project TimeGenerated, Computer, Subjectnombre de usuario, PrivilegeList, Severity, Tactics, Techniques

Near Real-Time (NRT) Rules

// Real-time malware detection
SecurityEvent
|where EventID == 4688
|where proceso has_any ("mimikatz", "procdump", "pwdump", "fgdump")
|extend
    Severity = "High",
    Tactics = "CredentialAccess",
    Techniques = "T1003"
|project TimeGenerated, Computer, Account, proceso, comandoLine, Severity, Tactics, Techniques

// Immediate threat response
SecurityEvent
|where EventID == 4625
|where Account == "Administrator"
|where IpAddress !startswith "192.168." and IpAddress !startswith "10." and IpAddress !startswith "172."
|extend
    Severity = "Critical",
    Tactics = "InitialAccess",
    Techniques = "T1078"
|project TimeGenerated, Computer, Account, IpAddress, Severity, Tactics, Techniques

Machine Learning Rules

// Anomaly detection template
let training_data =
    SecurityEvent
|where TimeGenerated between (ago(30d) .. ago(1d))
|where EventID == 4624
|summarize LoginCount = count() by Account, bin(TimeGenerated, 1h)
|summarize AvgLogins = avg(LoginCount), StdDev = stdev(LoginCount) by Account;
SecurityEvent
|where TimeGenerated > ago(1h)
|where EventID == 4624
|summarize CurrentLogins = count() by Account
|join kind=inner training_data on Account
|extend AnomalyScore = abs(CurrentLogins - AvgLogins) / StdDev
|where AnomalyScore > 3
|extend
    Severity = case(AnomalyScore > 5, "High", "Medium"),
    Tactics = "InitialAccess",
    Techniques = "T1078"
|project Account, CurrentLogins, AvgLogins, AnomalyScore, Severity, Tactics, Techniques

Incident Management and Investigation

Incident Creation and Assignment

# Create incident via PowerShell
$incidentParams = @\\\\{
    ResourceGroupName = "sentinel-rg"
    WorkspaceName = "sentinel-workspace"
    IncidentId = "incident-001"
    Title = "Suspicious Login Activity Detected"
    Descripción = "Multiple failed login attempts from external IP address"
    Severity = "High"
    Status = "New"
    Owner = @\\\\{
        email = "analyst@company.com"
        assignedTo = "Security Analyst"
        objectId = "<user-object-id>"
    \\\\}
    Labels = @(
        @\\\\{ labelName = "BruteForce"; labelType = "User" \\\\}
        @\\\\{ labelName = "ExternalIP"; labelType = "User" \\\\}
    )
\\\\}

New-AzSentinelIncident @incidentParams

# Update incident status
$updateParams = @\\\\{
    ResourceGroupName = "sentinel-rg"
    WorkspaceName = "sentinel-workspace"
    IncidentId = "incident-001"
    Status = "Active"
    Classification = "TruePositive"
    ClassificationComment = "Confirmed malicious activity"
    ClassificationReason = "SuspiciousActivity"
\\\\}

Update-AzSentinelIncident @updateParams

Investigation Queries

// Timeline reconstruction
let incident_time = datetime(2023-01-01T10:00:00Z);
let suspect_user = "john.doe@company.com";
let time_window = 2h;
union
| (SecurityEvent | where TimeGenerated between ((incident_time - time_window) .. (incident_time + time_window)) | where Account contains suspect_user), |
| (SigninLogs | where TimeGenerated between ((incident_time - time_window) .. (incident_time + time_window)) | where UserPrincipalName == suspect_user), |
| (AuditLogs | where TimeGenerated between ((incident_time - time_window) .. (incident_time + time_window)) | where InitiatedBy.user.userPrincipalName == suspect_user) |
|sort by TimeGenerated asc
|project TimeGenerated, Type, Activity, Account, Computer, IPAddress, Location

// Entity behavior analysis
let entity_ip = "192.168.1.100";
union
    (SecurityEvent|where IpAddress == entity_ip),
    (CommonSecurityLog|where SourceIP == entity_ip or DestinationIP == entity_ip),
    (DnsEvents|where ClientIP == entity_ip),
    (NetworkCommunicationEvents|where LocalIP == entity_ip or RemoteIP == entity_ip)
|summarize
    EventCount = count(),
    FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated),
    EventTypes = make_set(Type),
    UniqueDestinations = dcount(DestinationIP)
    by SourceIP
|sort by EventCount desc

// movimiento lateral tracking
let start_computer = "WORKSTATION-01";
SecurityEvent
|where EventID == 4624
|where LogonType in (3, 10)
|where Computer == start_computer
|extend NextHop = Computer
|join kind=inner (
    SecurityEvent
|where EventID == 4624
|where LogonType in (3, 10)
|where Computer != start_computer
) on Account
|where TimeGenerated1 > TimeGenerated
|project Account, SourceComputer = Computer, DestinationComputer = Computer1, TimeGenerated, TimeGenerated1
|sort by Account, TimeGenerated asc

Evidence Collection

// Collect relevant artifacts
let investigation_timeframe = ago(24h);
let entities = dynamic(["suspicious.user@company.com", "192.168.1.100", "COMPROMISED-PC"]);
union
    (SecurityEvent
|where TimeGenerated > investigation_timeframe
|where Account has_any (entities) or Computer has_any (entities) or IpAddress has_any (entities)),
    (SigninLogs
|where TimeGenerated > investigation_timeframe
|where UserPrincipalName has_any (entities) or IPAddress has_any (entities)),
    (DeviceEvents
|where TimeGenerated > investigation_timeframe
|where DeviceName has_any (entities) or InitiatingprocesoAccountName has_any (entities)),
    (EmailEvents
|where TimeGenerated > investigation_timeframe
|where SenderFromAddress has_any (entities) or RecipientEmailAddress has_any (entities))
|sort by TimeGenerated desc
|take 1000

// File hash analysis
DeviceFileEvents
|where TimeGenerated > ago(7d)
|where SHA256 in ("hash1", "hash2", "hash3")
|summarize
    FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated),
    DeviceCount = dcount(DeviceName),
    Devices = make_set(DeviceName)
    by SHA256, FileName
|sort by DeviceCount desc

Automation and Orchestration (SOAR)

Logic Apps Integration

\\\\{
    "definition": \\\\{
        "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
        "contentVersion": "1.0.0.0",
        "parámetros": \\\\{\\\\},
        "triggers": \\\\{
            "Microsoft_Sentinel_incident": \\\\{
                "type": "ApiconexiónWebhook",
                "inputs": \\\\{
                    "host": \\\\{
                        "conexión": \\\\{
                            "name": "@parámetros('$conexións')['azuresentinel']['conexiónId']"
                        \\\\}
                    \\\\},
                    "body": \\\\{
                        "callback_url": "@\\\\{listCallbackUrl()\\\\}"
                    \\\\},
                    "path": "/incident-creation"
                \\\\}
            \\\\}
        \\\\},
        "actions": \\\\{
            "Get_incident": \\\\{
                "type": "Apiconexión",
                "inputs": \\\\{
                    "host": \\\\{
                        "conexión": \\\\{
                            "name": "@parámetros('$conexións')['azuresentinel']['conexiónId']"
                        \\\\}
                    \\\\},
                    "method": "get",
                    "path": "/Incidents/subscriptions/@\\\\{encodeURIComponent(triggerBody()?['WorkspaceSubscriptionId'])\\\\}/resourceGroups/@\\\\{encodeURIComponent(triggerBody()?['WorkspaceResourceGroup'])\\\\}/workspaces/@\\\\{encodeURIComponent(triggerBody()?['WorkspaceId'])\\\\}/alerts/@\\\\{encodeURIComponent(triggerBody()?['SystemAlertId'])\\\\}"
                \\\\}
            \\\\},
            "Block_IP_in_firewall": \\\\{
                "type": "Http",
                "inputs": \\\\{
                    "method": "POST",
                    "uri": "https://firewall-api.company.com/block",
                    "headers": \\\\{
                        "autorización": "Bearer @\\\\{parámetros('firewall_token')\\\\}"
                    \\\\},
                    "body": \\\\{
                        "ip_address": "@\\\\{body('Get_incident')?['properties']?['relatedEntities'][0]?['properties']?['address']\\\\}",
                        "reason": "Blocked by Sentinel automation",
                        "duration": "24h"
                    \\\\}
                \\\\}
            \\\\},
            "Send_notification": \\\\{
                "type": "Apiconexión",
                "inputs": \\\\{
                    "host": \\\\{
                        "conexión": \\\\{
                            "name": "@parámetros('$conexións')['teams']['conexiónId']"
                        \\\\}
                    \\\\},
                    "method": "post",
                    "path": "/v1.0/teams/@\\\\{encodeURIComponent('security-team-id')\\\\}/channels/@\\\\{encodeURIComponent('incidents')\\\\}/messages",
                    "body": \\\\{
                        "body": \\\\{
                            "content": "🚨 High severity incident detected: @\\\\{body('Get_incident')?['properties']?['title']\\\\}\n\nIncident ID: @\\\\{body('Get_incident')?['properties']?['incidentNumber']\\\\}\nSeverity: @\\\\{body('Get_incident')?['properties']?['severity']\\\\}\nStatus: @\\\\{body('Get_incident')?['properties']?['status']\\\\}\n\nAutomatic response: IP address blocked in firewall"
                        \\\\}
                    \\\\}
                \\\\}
            \\\\}
        \\\\}
    \\\\}
\\\\}

PowerShell Automation

# Automated respuesta a incidentes script
param(
    [parámetro(Mandatory=$true)]
    [string]$IncidentId,

    [parámetro(Mandatory=$true)]
    [string]$WorkspaceName,

    [parámetro(Mandatory=$true)]
    [string]$ResourceGroupName
)

# Connect to Azure
Connect-AzAccount -Identity

# Get incident details
$incident = Get-AzSentinelIncident -ResourceGroupName $ResourceGroupName -WorkspaceName $WorkspaceName -IncidentId $IncidentId

# Extract entities
$entities = $incident.RelatedEntities

# proceso each entity
foreach ($entity in $entities) \\\\{
    switch ($entity.Kind) \\\\{
        "Account" \\\\{
            # Disable user account
            $userPrincipalName = $entity.Properties.Name
            Write-Output "Disabling user account: $userPrincipalName"

            # Connect to Azure AD
            Connect-AzureAD
            $user = Get-AzureADUser -Filter "userPrincipalName eq '$userPrincipalName'"
            if ($user) \\\\{
                Set-AzureADUser -ObjectId $user.ObjectId -AccountEnabled $false
                Write-Output "User account disabled successfully"
            \\\\}
        \\\\}

        "Ip" \\\\{
            # Block IP address
            $ipAddress = $entity.Properties.Address
            Write-Output "Blocking IP address: $ipAddress"

            # Add to Azure Firewall block list
            $firewallPolicy = Get-AzFirewallPolicy -ResourceGroupName $ResourceGroupName -Name "SecurityPolicy"
            $blockRule = New-AzFirewallPolicyNetworkRule -Name "Block-$ipAddress" -protocolo "Any" -SourceAddress $ipAddress -DestinationAddress "*" -Destinationpuerto "*" -Action "Deny"

            # Update firewall policy
            $ruleCollection = $firewallPolicy.RuleCollectionGroups[0].Properties.RuleCollections[0]
            $ruleCollection.Rules += $blockRule
            Set-AzFirewallPolicy -InputObject $firewallPolicy
            Write-Output "IP address blocked successfully"
        \\\\}

        "host" \\\\{
            # Isolate host
            $hostName = $entity.Properties.hostName
            Write-Output "Isolating host: $hostName"

            # Microsoft Defender for Endpoint isolation
            $isolationRequest = @\\\\{
                Comment = "Automated isolation due to security incident $IncidentId"
                IsolationType = "Full"
            \\\\}

            # Call Defender API to isolate machine
            $headers = @\\\\{
                'autorización' = "Bearer $($env:DEFENDER_token)"
                'Content-Type' = 'application/json'
            \\\\}

            Invoke-RestMethod -Uri "https://api.securitycenter.microsoft.com/api/machines/$hostName/isolate" -Method Post -Headers $headers -Body ($isolationRequest|ConvertTo-Json)
            Write-Output "host isolated successfully"
        \\\\}
    \\\\}
\\\\}

# Update incident with automation results
$updateComment = "Automated response completed: Users disabled, IPs blocked, hosts isolated"
Add-AzSentinelIncidentComment -ResourceGroupName $ResourceGroupName -WorkspaceName $WorkspaceName -IncidentId $IncidentId -Message $updateComment

# Update incident status
Update-AzSentinelIncident -ResourceGroupName $ResourceGroupName -WorkspaceName $WorkspaceName -IncidentId $IncidentId -Status "Active" -Classification "TruePositive"

Write-Output "respuesta a incidentes automation completed successfully"

Custom Connectors

# Custom Inteligencia de Amenazas connector
impuerto requests
impuerto json
impuerto hashlib
impuerto hmac
impuerto base64
impuerto datetime
from azure.identity impuerto DefaultAzureCredential
from azure.clavevault.secrets impuerto SecretClient

class SentinelConnector:
    def __init__(self, workspace_id, shared_clave):
        self.workspace_id = workspace_id
        self.shared_clave = shared_clave
        self.log_type = "ThreatIntelligence"

    def build_firma(self, date, content_length, method, content_type, resource):
        x_headers = f"x-ms-date:\\\\{date\\\\}"
        string_to_hash = f"\\\\{method\\\\}\n\\\\{content_length\\\\}\n\\\\{content_type\\\\}\n\\\\{x_headers\\\\}\n\\\\{resource\\\\}"
        bytes_to_hash = bytes(string_to_hash, 'utf-8')
        decoded_clave = base64.b64decode(self.shared_clave)
        encoded_hash = base64.b64encode(hmac.new(decoded_clave, bytes_to_hash, digestmod=hashlib.sha256).digest()).decode()
        autorización = f"Sharedclave \\\\{self.workspace_id\\\\}:\\\\{encoded_hash\\\\}"
        return autorización

    def send_data(self, body):
        method = 'POST'
        content_type = 'application/json'
        resource = '/api/logs'
        rfc1123date = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
        content_length = len(body)

        firma = self.build_firma(rfc1123date, content_length, method, content_type, resource)

        uri = f"https://\\\\{self.workspace_id\\\\}.ods.opinsights.azure.com\\\\{resource\\\\}?api-version=2016-04-01"

        headers = \\\\{
            'content-type': content_type,
            'autorización': firma,
            'Log-Type': self.log_type,
            'x-ms-date': rfc1123date
        \\\\}

        response = requests.post(uri, data=body, headers=headers)
        return response.status_code

# Inteligencia de Amenazas integration
def collect_threat_intelligence():
    # Collect from multiple sources
    threat_data = []

    # VirusTotal integration
    vt_api_clave = "your_virustotal_api_clave"
    vt_url = "https://www.virustotal.com/vtapi/v2/file/repuerto"

    file_hashes = ["hash1", "hash2", "hash3"]

    for file_hash in file_hashes:
        params = \\\\{'apiclave': vt_api_clave, 'resource': file_hash\\\\}
        response = requests.get(vt_url, params=params)

        if response.status_code == 200:
            vt_data = response.json()
            threat_data.append(\\\\{
                "timestamp": datetime.datetime.utcnow().isoformat(),
                "source": "VirusTotal",
                "indicator_type": "file_hash",
                "indicator_value": file_hash,
                "threat_type": "malware" if vt_data.get('positives', 0) > 0 else "clean",
                "confidence": vt_data.get('positives', 0) / vt_data.get('total', 1) * 100,
                "raw_data": json.dumps(vt_data)
            \\\\})

    # MISP integration
    misp_url = "https://your-misp-instance.com"
    misp_clave = "your_misp_api_clave"

    misp_headers = \\\\{
        'autorización': misp_clave,
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    \\\\}

    misp_response = requests.get(f"\\\\{misp_url\\\\}/attributes/restSearch", headers=misp_headers)

    if misp_response.status_code == 200:
        misp_data = misp_response.json()
        for attribute in misp_data.get('response', \\\\{\\\\}).get('Attribute', []):
            threat_data.append(\\\\{
                "timestamp": datetime.datetime.utcnow().isoformat(),
                "source": "MISP",
                "indicator_type": attribute.get('type'),
                "indicator_value": attribute.get('value'),
                "threat_type": attribute.get('category'),
                "confidence": 85,
                "raw_data": json.dumps(attribute)
            \\\\})

    return threat_data

# Send to Sentinel
def main():
    workspace_id = "your_workspace_id"
    shared_clave = "your_shared_clave"

    connector = SentinelConnector(workspace_id, shared_clave)
    threat_data = collect_threat_intelligence()

    if threat_data:
        body = json.dumps(threat_data)
        response_code = connector.send_data(body)
        print(f"Data sent to Sentinel. Response code: \\\\{response_code\\\\}")
    else:
        print("No Inteligencia de Amenazas data to send")

if __name__ == "__main__":
    main()

Workbooks and Visualization

Security Operations Workbook

// Security overview dashboard queries
// Failed logins by location
SigninLogs
|where TimeGenerated > ago(24h)
|where ResultType != "0"
|summarize FailedLogins = count() by Location
|sort by FailedLogins desc
|take 10

// Top attacked users
SecurityEvent
|where TimeGenerated > ago(24h)
|where EventID == 4625
|summarize FailedAttempts = count() by Account
|sort by FailedAttempts desc
|take 10

// Security alerts by severity
SecurityAlert
|where TimeGenerated > ago(7d)
|summarize AlertCount = count() by AlertSeverity
|render piechart

// respuesta a incidentes metrics
SecurityIncident
|where TimeGenerated > ago(30d)
|extend TimeToClose = iff(Status == "Closed", ClosedTime - CreatedTime, now() - CreatedTime)
|summarize
    AvgTimeToClose = avg(TimeToClose),
    TotalIncidents = count(),
    OpenIncidents = countif(Status != "Closed")
    by bin(TimeGenerated, 1d)
|render timechart

caza de amenazas Workbook

// Suspicious proceso execution timeline
SecurityEvent
|where TimeGenerated > ago(7d)
|where EventID == 4688
|where proceso has_any ("powershell.exe", "cmd.exe", "wscript.exe", "cscript.exe")
|where comandoLine has_any ("Invoke-", "Download", "http", "ftp", "base64")
|summarize procesoCount = count() by bin(TimeGenerated, 1h), proceso
|render timechart

// Network communication patterns
CommonSecurityLog
|where TimeGenerated > ago(24h)
|where DeviceVendor == "Palo Alto Networks"
|where Activity == "TRAFFIC"
|summarize
    TotalBytes = sum(SentBytes + ReceivedBytes),
    sesiónCount = count()
    by SourceIP, DestinationIP
|where TotalBytes > 1000000000  // 1GB threshold
|sort by TotalBytes desc

// DNS query analysis
DnsEvents
|where TimeGenerated > ago(24h)
|where QueryType in ("TXT", "NULL", "CNAME")
|summarize QueryCount = count() by Name, ClientIP
|where QueryCount > 100
|sort by QueryCount desc

// File creation in suspicious locations
SecurityEvent
|where TimeGenerated > ago(24h)
|where EventID == 4663
|where ObjectName has_any ("\\temp\\", "\\appdata\\", "\\programdata\\")
|where ObjectName endswith ".exe" or ObjectName endswith ".dll" or ObjectName endswith ".bat"
|summarize FileCount = count() by Computer, Account, ObjectName
|sort by FileCount desc

Performance Optimization and Best Practices

Query Optimization

// Optimized query structure
SecurityEvent
|where TimeGenerated > ago(1h)  // Filter time first
|where EventID == 4624          // Filter specific events
|where LogonType == 2           // Additional filters early
|project TimeGenerated, Computer, Account, LogonType  // Project only needed columns
|summarize LoginCount = count() by Computer, Account
|where LoginCount > 10
|sort by LoginCount desc

// Use summarize instead of distinct when possible
// Less efficient
SecurityEvent
|where TimeGenerated > ago(1h)
|distinct Computer

// More efficient
SecurityEvent
|where TimeGenerated > ago(1h)
|summarize by Computer

// Optimize joins
// Use smaller table on the right side of join
let SmallTable =
    SecurityEvent
|where TimeGenerated > ago(1h)
|where EventID == 4624
|summarize by Account;
SecurityEvent
|where TimeGenerated > ago(1h)
|where EventID == 4625
|join kind=inner SmallTable on Account

Data Retention and Cost Management

// Monitor data ingestion by table
uso
|where TimeGenerated > ago(30d)
|where IsBillable == true
|summarize TotalVolumeGB = sum(Quantity) / 1000 by DataType
|sort by TotalVolumeGB desc

// Analyze query costs
// Check scan data volume
SecurityEvent
|where TimeGenerated > ago(1h)
|summarize count()
// This query scans all SecurityEvent data for 1 hour

// Optimize with specific filters
SecurityEvent
|where TimeGenerated > ago(1h)
|where EventID == 4624  // Reduces scan volume significantly
|summarize count()

// Set up data retention policies
// Configure via PowerShell
$retentionPolicy = @\\\\{
    ResourceGroupName = "sentinel-rg"
    WorkspaceName = "sentinel-workspace"
    TableName = "SecurityEvent"
    RetentionInDays = 90
\\\\}

Set-AzOperationalInsightsTable @retentionPolicy

Security and Compliance

// Monitor privileged access to Sentinel
AuditLogs
|where TimeGenerated > ago(24h)
|where Category == "RoleManagement"
|where ActivityDisplayName has_any ("Add member to role", "Remove member from role")
|where objetivoResources[0].displayName has "Sentinel"
|project TimeGenerated, InitiatedBy.user.userPrincipalName, ActivityDisplayName, objetivoResources[0].displayName

// Track data access patterns
LAQueryLogs
|where TimeGenerated > ago(7d)
|summarize
    QueryCount = count(),
    DataScannedGB = sum(DataScanned_MB) / 1000,
    UniqueQueries = dcount(QueryText)
    by AADEmail
|sort by DataScannedGB desc

// Compliance repuertoing
SecurityEvent
|where TimeGenerated > ago(30d)
|where EventID in (4624, 4625, 4648, 4672)
|summarize
    TotalEvents = count(),
    SuccessfulLogins = countif(EventID == 4624),
    FailedLogins = countif(EventID == 4625),
    PrivilegeUse = countif(EventID == 4672)
    by bin(TimeGenerated, 1d)
|extend ComplianceScore = (SuccessfulLogins * 1.0) / (SuccessfulLogins + FailedLogins) * 100
|project TimeGenerated, TotalEvents, ComplianceScore

solución de problemas and Maintenance

Common Issues and Solutions

// Check data connector health
Heartbeat
|where TimeGenerated > ago(1h)
|summarize LastHeartbeat = max(TimeGenerated) by Computer
|where LastHeartbeat < ago(15m)
|project Computer, LastHeartbeat, Status = "Missing"

// Verify data ingestion
uso
|where TimeGenerated > ago(1h)
|summarize IngestedMB = sum(Quantity) by DataType
|where IngestedMB == 0
|project DataType, Status = "No data ingested"

// Analytics rule performance
SecurityAlert
|where TimeGenerated > ago(24h)
|summarize AlertCount = count() by AlertName
|join kind=leftouter (
    SecurityIncident
|where TimeGenerated > ago(24h)
|summarize IncidentCount = count() by Title
) on $left.AlertName == $right.Title
|extend FalsePositiveRate = (AlertCount - IncidentCount) * 100.0 / AlertCount
|where FalsePositiveRate > 80
|project AlertName, AlertCount, IncidentCount, FalsePositiveRate

Performance Monitoring

// Query performance analysis
LAQueryLogs
|where TimeGenerated > ago(24h)
|where ResponseCode == 200
|summarize
    AvgDuration = avg(DurationMs),
    MaxDuration = max(DurationMs),
    QueryCount = count()
    by bin(TimeGenerated, 1h)
|render timechart

// Resource utilization
Perf
|where TimeGenerated > ago(1h)
|where ObjectName == "procesoor" and CounterName == "% procesoor Time"
|summarize AvgCPU = avg(CounterValue) by Computer
|where AvgCPU > 80

// Storage utilization
uso
|where TimeGenerated > ago(30d)
|summarize TotalGB = sum(Quantity) / 1000 by Solution
|sort by TotalGB desc

Resources