Collecter les journaux Cisco AMP for Endpoints

Compatible avec :

Ce document explique comment ingérer des journaux Cisco AMP for Endpoints dans Google Security Operations à l'aide d'Amazon S3. L'analyseur transforme les journaux bruts au format JSON en un format structuré conforme à l'UDM Chronicle. Il extrait les champs des objets JSON imbriqués, les mappe au schéma UDM, identifie les catégories d'événements, attribue des niveaux de gravité et génère finalement une sortie d'événement unifiée, en signalant les alertes de sécurité lorsque des conditions spécifiques sont remplies.

Avant de commencer

  • Une instance Google SecOps
  • Accès privilégié à la console Cisco AMP for Endpoints
  • Accès privilégié à AWS (S3, IAM, Lambda, EventBridge)

Collecter les prérequis de Cisco AMP for Endpoints (ID, clés API, ID d'organisation, jetons)

  1. Connectez-vous à la console Cisco AMP for Endpoints.
  2. Accédez à Comptes> Identifiants de l'API.
  3. Cliquez sur Nouvel identifiant API pour créer une clé API et un ID client.
  4. Fournissez les informations de configuration suivantes :
    • Nom de l'application : saisissez un nom (par exemple, Chronicle SecOps Integration).
    • Portée : sélectionnez Lecture seule pour l'interrogation d'événements de base ou Lecture et écriture si vous prévoyez de créer des flux d'événements.
  5. Cliquez sur Créer.
  6. Copiez et enregistrez les informations suivantes dans un emplacement sécurisé :
    • ID client de l'API tierce
    • Clé API
    • URL de base de l'API : selon votre région :
      • États-Unis : https://api.amp.cisco.com
      • UE : https://api.eu.amp.cisco.com
      • APJC : https://api.apjc.amp.cisco.com

Configurer un bucket AWS S3 et IAM pour Google SecOps

  1. Créez un bucket Amazon S3 en suivant ce guide de l'utilisateur : Créer un bucket.
  2. Enregistrez le Nom et la Région du bucket pour référence ultérieure (par exemple, cisco-amp-logs).
  3. Créez un utilisateur en suivant ce guide de l'utilisateur : Créer un utilisateur IAM.
  4. Sélectionnez l'utilisateur créé.
  5. Sélectionnez l'onglet Informations d'identification de sécurité.
  6. Cliquez sur Créer une clé d'accès dans la section Clés d'accès.
  7. Sélectionnez Service tiers comme Cas d'utilisation.
  8. Cliquez sur Suivant.
  9. Facultatif : Ajoutez un tag de description.
  10. Cliquez sur Créer une clé d'accès.
  11. Cliquez sur Télécharger le fichier CSV pour enregistrer la clé d'accès et la clé d'accès secrète pour référence ultérieure.
  12. Cliquez sur OK.
  13. Sélectionnez l'onglet Autorisations.
  14. Cliquez sur Ajouter des autorisations dans la section Règles relatives aux autorisations.
  15. Sélectionnez Ajouter des autorisations.
  16. Sélectionnez Joindre directement des règles.
  17. Recherchez la règle AmazonS3FullAccess.
  18. Sélectionnez la règle.
  19. Cliquez sur Suivant.
  20. Cliquez sur Ajouter des autorisations.

Configurer la stratégie et le rôle IAM pour les importations S3

  1. Dans la console AWS, accédez à IAM > Stratégies.
  2. Cliquez sur Créer une règle > onglet JSON.
  3. Saisissez la règle suivante :

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutObjects",
          "Effect": "Allow",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::cisco-amp-logs/*"
        },
        {
          "Sid": "AllowGetStateObject",
          "Effect": "Allow",
          "Action": "s3:GetObject",
          "Resource": "arn:aws:s3:::cisco-amp-logs/cisco-amp-events/state.json"
        }
      ]
    }
    
    • Remplacez cisco-amp-logs si vous avez saisi un autre nom de bucket.
  4. Cliquez sur Suivant > Créer une règle.

  5. Accédez à IAM > Rôles > Créer un rôle > Service AWS > Lambda.

  6. Associez la règle que vous venez de créer.

  7. Nommez le rôle cisco-amp-lambda-role, puis cliquez sur Créer un rôle.

Créer la fonction Lambda

  1. Dans la console AWS, accédez à Lambda > Fonctions > Créer une fonction.
  2. Cliquez sur Créer à partir de zéro.
  3. Fournissez les informations de configuration suivantes :

    Paramètre Valeur
    Nom cisco-amp-events-collector
    Durée d'exécution Python 3.13
    Architecture x86_64
    Rôle d'exécution cisco-amp-lambda-role
  4. Une fois la fonction créée, ouvrez l'onglet Code, supprimez le stub et saisissez le code suivant (cisco-amp-events-collector.py) :

    import json
    import boto3
    import urllib3
    import base64
    from datetime import datetime, timedelta
    import os
    import logging
    
    # Configure logging
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    
    # AWS S3 client and HTTP pool manager
    s3_client = boto3.client('s3')
    http = urllib3.PoolManager()
    
    def lambda_handler(event, context):
        """
        AWS Lambda handler to fetch Cisco AMP events and store them in S3
        """
    
        try:
            # Get environment variables
            s3_bucket = os.environ['S3_BUCKET']
            s3_prefix = os.environ['S3_PREFIX']
            state_key = os.environ['STATE_KEY']
            api_client_id = os.environ['AMP_CLIENT_ID']
            api_key = os.environ['AMP_API_KEY']
            api_base = os.environ['API_BASE']
    
            # Optional parameters
            page_size = int(os.environ.get('PAGE_SIZE', '500'))
            max_pages = int(os.environ.get('MAX_PAGES', '10'))
    
            logger.info(f"Starting Cisco AMP events collection for bucket: {s3_bucket}")
    
            # Get last run timestamp from state file
            last_timestamp = get_last_timestamp(s3_bucket, state_key)
            if not last_timestamp:
                last_timestamp = (datetime.utcnow() - timedelta(days=1)).isoformat() + 'Z'
    
            # Create Basic Auth header
            auth_header = base64.b64encode(f"{api_client_id}:{api_key}".encode()).decode()
            headers = {
                'Authorization': f'Basic {auth_header}',
                'Accept': 'application/json'
            }
    
            # Build initial API URL
            base_url = f"{api_base}/v1/events"
            next_url = f"{base_url}?limit={page_size}&start_date={last_timestamp}"
    
            all_events = []
            page_count = 0
    
            while next_url and page_count < max_pages:
                logger.info(f"Fetching page {page_count + 1} from: {next_url}")
    
                # Make API request using urllib3
                response = http.request('GET', next_url, headers=headers, timeout=60)
    
                if response.status != 200:
                    raise RuntimeError(f"API request failed: {response.status} {response.data[:256]!r}")
    
                data = json.loads(response.data.decode('utf-8'))
    
                # Extract events from response
                events = data.get('data', [])
                if events:
                    all_events.extend(events)
                    logger.info(f"Collected {len(events)} events from page {page_count + 1}")
    
                    # Check for next page
                    next_url = data.get('metadata', {}).get('links', {}).get('next')
                    page_count += 1
                else:
                    logger.info("No events found on current page")
                    break
    
            logger.info(f"Total events collected: {len(all_events)}")
    
            # Store events in S3 if any were collected
            if all_events:
                timestamp_str = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
                s3_key = f"{s3_prefix}cisco_amp_events_{timestamp_str}.ndjson"
    
                # Convert events to NDJSON format (one JSON object per line)
                ndjson_content = 'n'.join(json.dumps(event) for event in all_events)
    
                # Upload to S3
                s3_client.put_object(
                    Bucket=s3_bucket,
                    Key=s3_key,
                    Body=ndjson_content.encode('utf-8'),
                    ContentType='application/x-ndjson'
                )
    
                logger.info(f"Uploaded {len(all_events)} events to s3://{s3_bucket}/{s3_key}")
    
            # Update state file with current timestamp
            current_timestamp = datetime.utcnow().isoformat() + 'Z'
            update_state(s3_bucket, state_key, current_timestamp)
    
            return {
                'statusCode': 200,
                'body': json.dumps({
                    'message': 'Success',
                    'events_collected': len(all_events),
                    'pages_processed': page_count
                })
            }
    
        except Exception as e:
            logger.error(f"Error in lambda_handler: {str(e)}")
            return {
                'statusCode': 500,
                'body': json.dumps({
                    'error': str(e)
                })
            }
    
    def get_last_timestamp(bucket, state_key):
        """
        Get the last run timestamp from S3 state file
        """
        try:
            response = s3_client.get_object(Bucket=bucket, Key=state_key)
            state_data = json.loads(response['Body'].read().decode('utf-8'))
            return state_data.get('last_timestamp')
        except s3_client.exceptions.NoSuchKey:
            logger.info("No state file found, starting from 24 hours ago")
            return None
        except Exception as e:
            logger.warning(f"Error reading state file: {str(e)}")
            return None
    
    def update_state(bucket, state_key, timestamp):
        """
        Update the state file with the current timestamp
        """
        try:
            state_data = {
                'last_timestamp': timestamp,
                'updated_at': datetime.utcnow().isoformat() + 'Z'
            }
    
            s3_client.put_object(
                Bucket=bucket,
                Key=state_key,
                Body=json.dumps(state_data).encode('utf-8'),
                ContentType='application/json'
            )
    
            logger.info(f"Updated state file with timestamp: {timestamp}")
    
        except Exception as e:
            logger.error(f"Error updating state file: {str(e)}")
    
  5. Accédez à Configuration > Variables d'environnement.

  6. Cliquez sur Modifier > Ajouter une variable d'environnement.

  7. Saisissez les variables d'environnement fournies ci-dessous, en remplaçant par vos valeurs.

    Clé Exemple de valeur
    S3_BUCKET cisco-amp-logs
    S3_PREFIX cisco-amp-events/
    STATE_KEY cisco-amp-events/state.json
    AMP_CLIENT_ID <your-client-id>
    AMP_API_KEY <your-api-key>
    API_BASE https://api.amp.cisco.com (ou l'URL de votre région)
    PAGE_SIZE 500
    MAX_PAGES 10
  8. Une fois la fonction créée, restez sur sa page (ou ouvrez Lambda > Fonctions > cisco-amp-events-collector).

  9. Accédez à l'onglet Configuration.

  10. Dans le panneau Configuration générale, cliquez sur Modifier.

  11. Définissez le délai avant expiration sur 5 minutes (300 secondes), puis cliquez sur Enregistrer.

Créer une programmation EventBridge

  1. Accédez à Amazon EventBridge> Scheduler> Create schedule.
  2. Fournissez les informations de configuration suivantes :
    • Planning récurrent : Tarif (1 hour).
    • Cible : votre fonction Lambda cisco-amp-events-collector.
    • Nom : cisco-amp-events-collector-1h.
  3. Cliquez sur Créer la programmation.

Facultatif : Créez un utilisateur et des clés IAM en lecture seule pour Google SecOps

  1. Accédez à la console AWS> IAM> Utilisateurs> Ajouter des utilisateurs.
  2. Cliquez sur Add users (Ajouter des utilisateurs).
  3. Fournissez les informations de configuration suivantes :
    • Utilisateur : saisissez secops-reader.
    • Type d'accès : sélectionnez Clé d'accès – Accès programmatique.
  4. Cliquez sur Créer un utilisateur.
  5. Associez une stratégie de lecture minimale (personnalisée) : Utilisateurs > secops-reader > Autorisations > Ajouter des autorisations > Associer des stratégies directement > Créer une stratégie.
  6. Dans l'éditeur JSON, saisissez la stratégie suivante :

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["s3:GetObject"],
          "Resource": "arn:aws:s3:::cisco-amp-logs/*"
        },
        {
          "Effect": "Allow",
          "Action": ["s3:ListBucket"],
          "Resource": "arn:aws:s3:::cisco-amp-logs"
        }
      ]
    }
    
  7. Définissez le nom sur secops-reader-policy.

  8. Accédez à Créer une règle > recherchez/sélectionnez > Suivant > Ajouter des autorisations.

  9. Accédez à Identifiants de sécurité > Clés d'accès > Créer une clé d'accès.

  10. Téléchargez le CSV (ces valeurs sont saisies dans le flux).

Configurer un flux dans Google SecOps pour ingérer les journaux Cisco AMP for Endpoints

  1. Accédez à Paramètres SIEM> Flux.
  2. Cliquez sur + Ajouter un flux.
  3. Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple, Cisco AMP for Endpoints logs).
  4. Sélectionnez Amazon S3 V2 comme type de source.
  5. Sélectionnez Cisco AMP comme Type de journal.
  6. Cliquez sur Suivant.
  7. Spécifiez les valeurs des paramètres d'entrée suivants :
    • URI S3 : s3://cisco-amp-logs/cisco-amp-events/
    • Options de suppression de la source : sélectionnez l'option de suppression de votre choix.
    • Âge maximal des fichiers : incluez les fichiers modifiés au cours des derniers jours. La valeur par défaut est de 180 jours.
    • ID de clé d'accès : clé d'accès utilisateur ayant accès au bucket S3.
    • Clé d'accès secrète : clé secrète de l'utilisateur ayant accès au bucket S3.
    • Espace de noms de l'élément : espace de noms de l'élément.
    • Libellés d'ingestion : libellé appliqué aux événements de ce flux.
  8. Cliquez sur Suivant.
  9. Vérifiez la configuration de votre nouveau flux sur l'écran Finaliser, puis cliquez sur Envoyer.

Table de mappage UDM

Champ de journal Mappage UDM Logique
actif read_only_udm.principal.asset.active Mappé directement à partir de computer.active
connector_guid read_only_udm.principal.asset.uuid Mappé directement à partir de computer.connector_guid
date read_only_udm.metadata.event_timestamp.seconds Mappé directement à partir de date après conversion en code temporel
détection read_only_udm.security_result.threat_name Mappé directement à partir de detection
detection_id read_only_udm.security_result.detection_fields.value Mappé directement à partir de detection_id
disposition read_only_udm.security_result.description Mappé directement à partir de file.disposition
error.error_code read_only_udm.security_result.detection_fields.value Mappé directement à partir de error.error_code
error.description read_only_udm.security_result.detection_fields.value Mappé directement à partir de error.description
event_type read_only_udm.metadata.product_event_type Mappé directement à partir de event_type
event_type_id read_only_udm.metadata.product_log_id Mappé directement à partir de event_type_id
external_ip read_only_udm.principal.asset.external_ip Mappé directement à partir de computer.external_ip
file.file_name read_only_udm.target.file.names Mappé directement à partir de file.file_name
file.file_path read_only_udm.target.file.full_path Mappé directement à partir de file.file_path
file.identity.md5 read_only_udm.security_result.about.file.md5 Mappé directement à partir de file.identity.md5
file.identity.md5 read_only_udm.target.file.md5 Mappé directement à partir de file.identity.md5
file.identity.sha1 read_only_udm.security_result.about.file.sha1 Mappé directement à partir de file.identity.sha1
file.identity.sha1 read_only_udm.target.file.sha1 Mappé directement à partir de file.identity.sha1
file.identity.sha256 read_only_udm.security_result.about.file.sha256 Mappé directement à partir de file.identity.sha256
file.identity.sha256 read_only_udm.target.file.sha256 Mappé directement à partir de file.identity.sha256
file.parent.disposition read_only_udm.target.resource.attribute.labels.value Mappé directement à partir de file.parent.disposition
file.parent.file_name read_only_udm.target.resource.attribute.labels.value Mappé directement à partir de file.parent.file_name
file.parent.identity.md5 read_only_udm.target.resource.attribute.labels.value Mappé directement à partir de file.parent.identity.md5
file.parent.identity.sha1 read_only_udm.target.resource.attribute.labels.value Mappé directement à partir de file.parent.identity.sha1
file.parent.identity.sha256 read_only_udm.target.resource.attribute.labels.value Mappé directement à partir de file.parent.identity.sha256
file.parent.process_id read_only_udm.security_result.about.process.parent_process.pid Mappé directement à partir de file.parent.process_id
file.parent.process_id read_only_udm.target.process.parent_process.pid Mappé directement à partir de file.parent.process_id
nom d'hôte read_only_udm.principal.asset.hostname Mappé directement à partir de computer.hostname
nom d'hôte read_only_udm.target.hostname Mappé directement à partir de computer.hostname
nom d'hôte read_only_udm.target.asset.hostname Mappé directement à partir de computer.hostname
ip read_only_udm.principal.asset.ip Mappé directement à partir de computer.network_addresses.ip
ip read_only_udm.principal.ip Mappé directement à partir de computer.network_addresses.ip
ip read_only_udm.security_result.about.ip Mappé directement à partir de computer.network_addresses.ip
mac read_only_udm.principal.mac Mappé directement à partir de computer.network_addresses.mac
mac read_only_udm.security_result.about.mac Mappé directement à partir de computer.network_addresses.mac
de gravité, read_only_udm.security_result.severity Mappé à partir de severity selon la logique suivante :
 - "Medium" -> "MEDIUM"
 - "High" ou "Critical" -> "HIGH"
 - "Low" -> "LOW"
 - Sinon -> "UNKNOWN_SEVERITY"
timestamp read_only_udm.metadata.event_timestamp.seconds Mappé directement à partir de timestamp
utilisateur read_only_udm.security_result.about.user.user_display_name Mappé directement à partir de computer.user
utilisateur read_only_udm.target.user.user_display_name Mappé directement à partir de computer.user
vulnerabilities.cve read_only_udm.extensions.vulns.vulnerabilities.cve_id Mappé directement à partir de vulnerabilities.cve
vulnerabilities.name read_only_udm.extensions.vulns.vulnerabilities.name Mappé directement à partir de vulnerabilities.name
vulnerabilities.score read_only_udm.extensions.vulns.vulnerabilities.cvss_base_score Mappé directement à partir de vulnerabilities.score après conversion en float
vulnerabilities.url read_only_udm.extensions.vulns.vulnerabilities.vendor_knowledge_base_article_id Mappé directement à partir de vulnerabilities.url
vulnerabilities.version read_only_udm.extensions.vulns.vulnerabilities.cvss_version Mappé directement à partir de vulnerabilities.version
is_alert Définissez sur "true" si event_type est l'une des valeurs suivantes : "Threat Detected" (Menace détectée), "Exploit Prevention" (Prévention de l'exploitation), "Executed malware" (Logiciel malveillant exécuté), "Potential Dropper Infection" (Infection potentielle par un programme d'installation), "Multiple Infected Files" (Plusieurs fichiers infectés), "Vulnerable Application Detected" (Application vulnérable détectée) ou si security_result.severity est "HIGH" (ÉLEVÉ).
is_significant Définissez sur "true" si event_type est l'une des valeurs suivantes : "Threat Detected" (Menace détectée), "Exploit Prevention" (Prévention de l'exploitation), "Executed malware" (Logiciel malveillant exécuté), "Potential Dropper Infection" (Infection potentielle par un programme d'installation), "Multiple Infected Files" (Plusieurs fichiers infectés), "Vulnerable Application Detected" (Application vulnérable détectée) ou si security_result.severity est "HIGH" (ÉLEVÉ).
read_only_udm.metadata.event_type Déterminé en fonction des valeurs event_type et security_result.severity.
 : si event_type correspond à l'une des valeurs suivantes : "Executed malware", "Threat Detected", "Potential Dropper Infection", "Cloud Recall Detection", "Malicious Activity Detection", "Exploit Prevention", "Multiple Infected Files", "Cloud IOC", "System Process Protection", "Vulnerable Application Detected", "Threat Quarantined", "Execution Blocked", "Cloud Recall Quarantine Successful", "Cloud Recall Restore from Quarantine Failed", "Cloud Recall Quarantine Attempt Failed", "Quarantine Failure", le type d'événement est défini sur "SCAN_FILE".
 : si security_result.severity est défini sur "HIGH", le type d'événement est défini sur "SCAN_FILE".
 : si has_principal et has_target sont définis sur "true", le type d'événement est défini sur "SCAN_UNCATEGORIZED".
 : sinon, le type d'événement est défini sur "GENERIC_EVENT".
read_only_udm.metadata.log_type Définie sur "CISCO_AMP"
read_only_udm.metadata.vendor_name Définie sur "CISCO_AMP"
read_only_udm.security_result.about.file.full_path Mappé directement à partir de file.file_path
read_only_udm.security_result.about.hostname Mappé directement à partir de computer.hostname
read_only_udm.security_result.about.user.user_display_name Mappé directement à partir de computer.user
read_only_udm.security_result.detection_fields.key Définissez "ID de détection" pour detection_id, "Code d'erreur" pour error.error_code, "Description de l'erreur" pour error.description, "Disposition parente" pour file.parent.disposition, "Nom du fichier parent" pour file.parent.file_name, "MD5 parent" pour file.parent.identity.md5, "SHA1 parent" pour file.parent.identity.sha1 et "SHA256 parent" pour file.parent.identity.sha256.
read_only_udm.security_result.summary Définissez sur event_type si event_type est l'une des valeurs suivantes : "Menace détectée", "Prévention de l'exploitation", "Logiciel malveillant exécuté", "Infection potentielle par un programme d'installation", "Plusieurs fichiers infectés", "Application vulnérable détectée" ou si security_result.severity est défini sur "HIGH" (ÉLEVÉ).
read_only_udm.target.asset.ip Mappé directement à partir de computer.network_addresses.ip
read_only_udm.target.resource.attribute.labels.key Définissez "Disposition du parent" sur file.parent.disposition, "Nom de fichier parent" sur file.parent.file_name, "MD5 du parent" sur file.parent.identity.md5, "SHA1 du parent" sur file.parent.identity.sha1 et "SHA256 du parent" sur file.parent.identity.sha256.
timestamp.seconds Mappé directement à partir de date après conversion en code temporel

Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.