Collecter les journaux Cisco AMP for Endpoints
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)
- Connectez-vous à la console Cisco AMP for Endpoints.
- Accédez à Comptes> Identifiants de l'API.
- Cliquez sur Nouvel identifiant API pour créer une clé API et un ID client.
- 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.
- Nom de l'application : saisissez un nom (par exemple,
- Cliquez sur Créer.
- 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
- États-Unis :
Configurer un bucket AWS S3 et IAM pour Google SecOps
- Créez un bucket Amazon S3 en suivant ce guide de l'utilisateur : Créer un bucket.
- Enregistrez le Nom et la Région du bucket pour référence ultérieure (par exemple,
cisco-amp-logs). - Créez un utilisateur en suivant ce guide de l'utilisateur : Créer un utilisateur IAM.
- Sélectionnez l'utilisateur créé.
- Sélectionnez l'onglet Informations d'identification de sécurité.
- Cliquez sur Créer une clé d'accès dans la section Clés d'accès.
- Sélectionnez Service tiers comme Cas d'utilisation.
- Cliquez sur Suivant.
- Facultatif : Ajoutez un tag de description.
- Cliquez sur Créer une clé d'accès.
- 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.
- Cliquez sur OK.
- Sélectionnez l'onglet Autorisations.
- Cliquez sur Ajouter des autorisations dans la section Règles relatives aux autorisations.
- Sélectionnez Ajouter des autorisations.
- Sélectionnez Joindre directement des règles.
- Recherchez la règle AmazonS3FullAccess.
- Sélectionnez la règle.
- Cliquez sur Suivant.
- Cliquez sur Ajouter des autorisations.
Configurer la stratégie et le rôle IAM pour les importations S3
- Dans la console AWS, accédez à IAM > Stratégies.
- Cliquez sur Créer une règle > onglet JSON.
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-logssi vous avez saisi un autre nom de bucket.
- Remplacez
Cliquez sur Suivant > Créer une règle.
Accédez à IAM > Rôles > Créer un rôle > Service AWS > Lambda.
Associez la règle que vous venez de créer.
Nommez le rôle
cisco-amp-lambda-role, puis cliquez sur Créer un rôle.
Créer la fonction Lambda
- Dans la console AWS, accédez à Lambda > Fonctions > Créer une fonction.
- Cliquez sur Créer à partir de zéro.
Fournissez les informations de configuration suivantes :
Paramètre Valeur Nom cisco-amp-events-collectorDurée d'exécution Python 3.13 Architecture x86_64 Rôle d'exécution cisco-amp-lambda-roleUne 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)}")Accédez à Configuration > Variables d'environnement.
Cliquez sur Modifier > Ajouter une variable d'environnement.
Saisissez les variables d'environnement fournies ci-dessous, en remplaçant par vos valeurs.
Clé Exemple de valeur S3_BUCKETcisco-amp-logsS3_PREFIXcisco-amp-events/STATE_KEYcisco-amp-events/state.jsonAMP_CLIENT_ID<your-client-id>AMP_API_KEY<your-api-key>API_BASEhttps://api.amp.cisco.com(ou l'URL de votre région)PAGE_SIZE500MAX_PAGES10Une fois la fonction créée, restez sur sa page (ou ouvrez Lambda > Fonctions > cisco-amp-events-collector).
Accédez à l'onglet Configuration.
Dans le panneau Configuration générale, cliquez sur Modifier.
Définissez le délai avant expiration sur 5 minutes (300 secondes), puis cliquez sur Enregistrer.
Créer une programmation EventBridge
- Accédez à Amazon EventBridge> Scheduler> Create schedule.
- 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.
- Planning récurrent : Tarif (
- Cliquez sur Créer la programmation.
Facultatif : Créez un utilisateur et des clés IAM en lecture seule pour Google SecOps
- Accédez à la console AWS> IAM> Utilisateurs> Ajouter des utilisateurs.
- Cliquez sur Add users (Ajouter des utilisateurs).
- Fournissez les informations de configuration suivantes :
- Utilisateur : saisissez
secops-reader. - Type d'accès : sélectionnez Clé d'accès – Accès programmatique.
- Utilisateur : saisissez
- Cliquez sur Créer un utilisateur.
- 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.
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" } ] }Définissez le nom sur
secops-reader-policy.Accédez à Créer une règle > recherchez/sélectionnez > Suivant > Ajouter des autorisations.
Accédez à Identifiants de sécurité > Clés d'accès > Créer une clé d'accès.
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
- Accédez à Paramètres SIEM> Flux.
- Cliquez sur + Ajouter un flux.
- Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple,
Cisco AMP for Endpoints logs). - Sélectionnez Amazon S3 V2 comme type de source.
- Sélectionnez Cisco AMP comme Type de journal.
- Cliquez sur Suivant.
- 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.
- URI S3 :
- Cliquez sur Suivant.
- 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.