Recoger registros de Cisco AMP para endpoints
En este documento se explica cómo ingerir registros de Cisco AMP for Endpoints en Google Security Operations mediante Amazon S3. El analizador transforma los registros sin procesar en formato JSON en un formato estructurado conforme al UDM de Chronicle. Extrae campos de objetos JSON anidados, los asigna al esquema de UDM, identifica categorías de eventos, asigna niveles de gravedad y, en última instancia, genera una salida de eventos unificada, marcando alertas de seguridad cuando se cumplen condiciones específicas.
Antes de empezar
- Una instancia de Google SecOps
- Acceso privilegiado a la consola de Cisco AMP for Endpoints
- Acceso privilegiado a AWS (S3, IAM, Lambda y EventBridge)
Recopilar los requisitos previos de Cisco AMP para endpoints (IDs, claves de API, IDs de organización y tokens)
- Inicia sesión en la consola de Cisco AMP for Endpoints.
- Ve a Cuentas > Credenciales de API.
- Haz clic en Nueva credencial de API para crear una clave de API y un ID de cliente.
- Proporcione los siguientes detalles de configuración:
- Nombre de la aplicación: introduce un nombre (por ejemplo,
Chronicle SecOps Integration). - Ámbito: selecciona Solo lectura para la sondeo de eventos básico o Lectura y escritura si tienes previsto crear flujos de eventos.
- Nombre de la aplicación: introduce un nombre (por ejemplo,
- Haz clic en Crear.
- Copia y guarda en un lugar seguro los siguientes detalles:
- ID de cliente de API de terceros
- Clave de API
- URL base de la API: según tu región:
- EE. UU.:
https://api.amp.cisco.com - UE:
https://api.eu.amp.cisco.com - APJC:
https://api.apjc.amp.cisco.com
- EE. UU.:
Configurar un segmento de AWS S3 y IAM para Google SecOps
- Crea un segmento de Amazon S3 siguiendo esta guía de usuario: Crear un segmento.
- Guarda el nombre y la región del segmento para consultarlos más adelante (por ejemplo,
cisco-amp-logs). - Crea un usuario siguiendo esta guía: Crear un usuario de gestión de identidades y accesos.
- Selecciona el Usuario creado.
- Selecciona la pestaña Credenciales de seguridad.
- En la sección Claves de acceso, haz clic en Crear clave de acceso.
- Selecciona Servicio de terceros en Caso práctico.
- Haz clic en Siguiente.
- Opcional: añade una etiqueta de descripción.
- Haz clic en Crear clave de acceso.
- Haz clic en Descargar archivo CSV para guardar la clave de acceso y la clave de acceso secreta para futuras consultas.
- Haz clic en Listo.
- Selecciona la pestaña Permisos.
- En la sección Políticas de permisos, haz clic en Añadir permisos.
- Selecciona Añadir permisos.
- Seleccione Adjuntar políticas directamente.
- Busca la política AmazonS3FullAccess.
- Selecciona la política.
- Haz clic en Siguiente.
- Haz clic en Añadir permisos.
Configurar la política y el rol de gestión de identidades y accesos para las subidas de S3
- En la consola de AWS, ve a IAM > Políticas.
- Haz clic en Crear política > pestaña JSON.
Introduce la siguiente política:
{ "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" } ] }- Sustituye
cisco-amp-logssi has introducido otro nombre de segmento.
- Sustituye
Haz clic en Siguiente > Crear política.
Ve a IAM > Roles > Crear rol > Servicio de AWS > Lambda.
Adjunte la política que acaba de crear.
Dale el nombre
cisco-amp-lambda-roleal rol y haz clic en Crear rol.
Crear la función Lambda
- En la consola de AWS, ve a Lambda > Funciones > Crear función.
- Haz clic en Crear desde cero.
Proporciona los siguientes detalles de configuración:
Ajuste Valor Nombre cisco-amp-events-collectorTiempo de ejecución Python 3.13 Arquitectura x86_64 Rol de ejecución cisco-amp-lambda-roleUna vez creada la función, abra la pestaña Código, elimine el stub e introduzca el siguiente código (
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)}")Vaya a Configuración > Variables de entorno.
Haz clic en Editar > Añadir nueva variable de entorno.
Introduce las siguientes variables de entorno, sustituyendo los valores por los tuyos.
Clave Valor de ejemplo 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(o la URL de tu región)PAGE_SIZE500MAX_PAGES10Una vez creada la función, permanece en su página (o abre Lambda > Funciones > cisco-amp-events-collector).
Seleccione la pestaña Configuración.
En el panel Configuración general, haz clic en Editar.
Cambia Tiempo de espera a 5 minutos (300 segundos) y haz clic en Guardar.
Crear una programación de EventBridge
- Ve a Amazon EventBridge > Scheduler > Create schedule (Amazon EventBridge > Programador > Crear programación).
- Proporcione los siguientes detalles de configuración:
- Programación periódica: Precio (
1 hour). - Destino: tu función Lambda
cisco-amp-events-collector. - Nombre:
cisco-amp-events-collector-1h.
- Programación periódica: Precio (
- Haz clic en Crear programación.
Opcional: Crear un usuario y claves de gestión de identidades y accesos de solo lectura para Google SecOps
- Ve a Consola de AWS > IAM > Usuarios > Añadir usuarios.
- Haz clic en Add users (Añadir usuarios).
- Proporcione los siguientes detalles de configuración:
- Usuario: introduce
secops-reader. - Tipo de acceso: selecciona Clave de acceso – Acceso programático.
- Usuario: introduce
- Haz clic en Crear usuario.
- Asigna una política de lectura mínima (personalizada): Usuarios > secops-reader > Permisos > Añadir permisos > Asignar políticas directamente > Crear política.
En el editor de JSON, introduce la siguiente política:
{ "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" } ] }Asigna el nombre
secops-reader-policy.Ve a Crear política > busca o selecciona > Siguiente > Añadir permisos.
Ve a Credenciales de seguridad > Claves de acceso > Crear clave de acceso.
Descarga el archivo CSV (estos valores se introducen en el feed).
Configurar un feed en Google SecOps para ingerir registros de Cisco AMP for Endpoints
- Ve a Configuración de SIEM > Feeds.
- Haz clic en + Añadir nuevo feed.
- En el campo Nombre del feed, introduce un nombre para el feed (por ejemplo,
Cisco AMP for Endpoints logs). - Selecciona Amazon S3 V2 como Tipo de fuente.
- Selecciona Cisco AMP como Tipo de registro.
- Haz clic en Siguiente.
- Especifique los valores de los siguientes parámetros de entrada:
- URI de S3:
s3://cisco-amp-logs/cisco-amp-events/ - Opciones de eliminación de la fuente: selecciona la opción de eliminación que prefieras.
- Antigüedad máxima del archivo: incluye los archivos modificados en los últimos días. El valor predeterminado es 180 días.
- ID de clave de acceso: clave de acceso de usuario con acceso al bucket de S3.
- Clave de acceso secreta: clave secreta del usuario con acceso al bucket de S3.
- Espacio de nombres de recursos: el espacio de nombres de recursos.
- Etiquetas de ingestión: la etiqueta aplicada a los eventos de este feed.
- URI de S3:
- Haz clic en Siguiente.
- Revise la configuración de la nueva fuente en la pantalla Finalizar y, a continuación, haga clic en Enviar.
Tabla de asignación de UDM
| Campo de registro | Asignación de UDM | Lógica |
|---|---|---|
| activa | read_only_udm.principal.asset.active | Asignado directamente desde computer.active |
| connector_guid | read_only_udm.principal.asset.uuid | Asignado directamente desde computer.connector_guid |
| fecha | read_only_udm.metadata.event_timestamp.seconds | Se asigna directamente desde date después de convertirlo en una marca de tiempo. |
| detección | read_only_udm.security_result.threat_name | Asignado directamente desde detection |
| detection_id | read_only_udm.security_result.detection_fields.value | Asignado directamente desde detection_id |
| disposition | read_only_udm.security_result.description | Asignado directamente desde file.disposition |
| error.error_code | read_only_udm.security_result.detection_fields.value | Asignado directamente desde error.error_code |
| error.description | read_only_udm.security_result.detection_fields.value | Asignado directamente desde error.description |
| event_type | read_only_udm.metadata.product_event_type | Asignado directamente desde event_type |
| event_type_id | read_only_udm.metadata.product_log_id | Asignado directamente desde event_type_id |
| external_ip | read_only_udm.principal.asset.external_ip | Asignado directamente desde computer.external_ip |
| file.file_name | read_only_udm.target.file.names | Asignado directamente desde file.file_name |
| file.file_path | read_only_udm.target.file.full_path | Asignado directamente desde file.file_path |
| file.identity.md5 | read_only_udm.security_result.about.file.md5 | Asignado directamente desde file.identity.md5 |
| file.identity.md5 | read_only_udm.target.file.md5 | Asignado directamente desde file.identity.md5 |
| file.identity.sha1 | read_only_udm.security_result.about.file.sha1 | Asignado directamente desde file.identity.sha1 |
| file.identity.sha1 | read_only_udm.target.file.sha1 | Asignado directamente desde file.identity.sha1 |
| file.identity.sha256 | read_only_udm.security_result.about.file.sha256 | Asignado directamente desde file.identity.sha256 |
| file.identity.sha256 | read_only_udm.target.file.sha256 | Asignado directamente desde file.identity.sha256 |
| file.parent.disposition | read_only_udm.target.resource.attribute.labels.value | Asignado directamente desde file.parent.disposition |
| file.parent.file_name | read_only_udm.target.resource.attribute.labels.value | Asignado directamente desde file.parent.file_name |
| file.parent.identity.md5 | read_only_udm.target.resource.attribute.labels.value | Asignado directamente desde file.parent.identity.md5 |
| file.parent.identity.sha1 | read_only_udm.target.resource.attribute.labels.value | Asignado directamente desde file.parent.identity.sha1 |
| file.parent.identity.sha256 | read_only_udm.target.resource.attribute.labels.value | Asignado directamente desde file.parent.identity.sha256 |
| file.parent.process_id | read_only_udm.security_result.about.process.parent_process.pid | Asignado directamente desde file.parent.process_id |
| file.parent.process_id | read_only_udm.target.process.parent_process.pid | Asignado directamente desde file.parent.process_id |
| nombre de host | read_only_udm.principal.asset.hostname | Asignado directamente desde computer.hostname |
| nombre de host | read_only_udm.target.hostname | Asignado directamente desde computer.hostname |
| nombre de host | read_only_udm.target.asset.hostname | Asignado directamente desde computer.hostname |
| ip | read_only_udm.principal.asset.ip | Asignado directamente desde computer.network_addresses.ip |
| ip | read_only_udm.principal.ip | Asignado directamente desde computer.network_addresses.ip |
| ip | read_only_udm.security_result.about.ip | Asignado directamente desde computer.network_addresses.ip |
| mac | read_only_udm.principal.mac | Asignado directamente desde computer.network_addresses.mac |
| mac | read_only_udm.security_result.about.mac | Asignado directamente desde computer.network_addresses.mac |
| gravedad | read_only_udm.security_result.severity | Se ha asignado desde severity según la siguiente lógica: - "Medium" (Media) -> "MEDIUM" (MEDIA) - "High" (Alta) o "Critical" (Crítica) -> "HIGH" (ALTA) - "Low" (Baja) -> "LOW" (BAJA) - En otros casos -> "UNKNOWN_SEVERITY" (GRAVEDAD_DESCONOCIDA) |
| timestamp | read_only_udm.metadata.event_timestamp.seconds | Asignado directamente desde timestamp |
| usuario | read_only_udm.security_result.about.user.user_display_name | Asignado directamente desde computer.user |
| usuario | read_only_udm.target.user.user_display_name | Asignado directamente desde computer.user |
| vulnerabilities.cve | read_only_udm.extensions.vulns.vulnerabilities.cve_id | Asignado directamente desde vulnerabilities.cve |
| vulnerabilities.name | read_only_udm.extensions.vulns.vulnerabilities.name | Asignado directamente desde vulnerabilities.name |
| vulnerabilities.score | read_only_udm.extensions.vulns.vulnerabilities.cvss_base_score | Se asigna directamente desde vulnerabilities.score después de convertirlo en un valor flotante. |
| vulnerabilities.url | read_only_udm.extensions.vulns.vulnerabilities.vendor_knowledge_base_article_id | Asignado directamente desde vulnerabilities.url |
| vulnerabilities.version | read_only_udm.extensions.vulns.vulnerabilities.cvss_version | Asignado directamente desde vulnerabilities.version |
| is_alert | Se define como true si event_type es uno de los siguientes valores: "Threat Detected" (Amenaza detectada), "Exploit Prevention" (Prevención de exploits), "Executed malware" (Malware ejecutado), "Potential Dropper Infection" (Posible infección de dropper), "Multiple Infected Files" (Varios archivos infectados) o "Vulnerable Application Detected" (Aplicación vulnerable detectada). También se define como true si security_result.severity es "HIGH" (ALTO). |
|
| is_significant | Se define como true si event_type es uno de los siguientes valores: "Threat Detected" (Amenaza detectada), "Exploit Prevention" (Prevención de exploits), "Executed malware" (Malware ejecutado), "Potential Dropper Infection" (Posible infección de dropper), "Multiple Infected Files" (Varios archivos infectados) o "Vulnerable Application Detected" (Aplicación vulnerable detectada). También se define como true si security_result.severity es "HIGH" (ALTO). |
|
| read_only_udm.metadata.event_type | Se determina en función de los valores de event_type y security_result.severity. : si event_type es uno de los siguientes valores: "Executed malware" (Malware ejecutado), "Threat Detected" (Amenaza detectada), "Potential Dropper Infection" (Posible infección por dropper), "Cloud Recall Detection" (Detección de Cloud Recall), "Malicious Activity Detection" (Detección de actividad maliciosa), "Exploit Prevention" (Prevención de exploits), "Multiple Infected Files" (Varios archivos infectados), "Cloud IOC" (IOC en la nube), "System Process Protection" (Protección de procesos del sistema), "Vulnerable Application Detected" (Aplicación vulnerable detectada), "Threat Quarantined" (Amenaza puesta en cuarentena), "Execution Blocked" (Ejecución bloqueada), "Cloud Recall Quarantine Successful" (Cuarentena de Cloud Recall completada), "Cloud Recall Restore from Quarantine Failed" (No se ha podido restaurar Cloud Recall desde la cuarentena), "Cloud Recall Quarantine Attempt Failed" (No se ha podido poner en cuarentena Cloud Recall) o "Quarantine Failure" (Fallo de cuarentena), el tipo de evento se define como "SCAN_FILE". : si security_result.severity es "HIGH", el tipo de evento se define como "SCAN_FILE". : si tanto has_principal como has_target son verdaderos, el tipo de evento se define como "SCAN_UNCATEGORIZED". : en caso contrario, el tipo de evento se define como "GENERIC_EVENT". |
|
| read_only_udm.metadata.log_type | Se ha definido como "CISCO_AMP" | |
| read_only_udm.metadata.vendor_name | Se ha definido como "CISCO_AMP" | |
| read_only_udm.security_result.about.file.full_path | Asignado directamente desde file.file_path |
|
| read_only_udm.security_result.about.hostname | Asignado directamente desde computer.hostname |
|
| read_only_udm.security_result.about.user.user_display_name | Asignado directamente desde computer.user |
|
| read_only_udm.security_result.detection_fields.key | Asigna el valor "Detection ID" a detection_id, "Error Code" a error.error_code, "Error Description" a error.description, "Parent Disposition" a file.parent.disposition, "Parent File Name" a file.parent.file_name, "Parent MD5" a file.parent.identity.md5, "Parent SHA1" a file.parent.identity.sha1 y "Parent SHA256" a file.parent.identity.sha256. |
|
| read_only_udm.security_result.summary | Se define como event_type si event_type es uno de los siguientes valores: "Threat Detected" (Amenaza detectada), "Exploit Prevention" (Prevención de exploits), "Executed malware" (Malware ejecutado), "Potential Dropper Infection" (Posible infección por dropper), "Multiple Infected Files" (Varios archivos infectados) o "Vulnerable Application Detected" (Aplicación vulnerable detectada), o si security_result.severity es "HIGH" (ALTO). |
|
| read_only_udm.target.asset.ip | Asignado directamente desde computer.network_addresses.ip |
|
| read_only_udm.target.resource.attribute.labels.key | Asigna el valor "Parent Disposition" a file.parent.disposition, "Parent File Name" a file.parent.file_name, "Parent MD5" a file.parent.identity.md5, "Parent SHA1" a file.parent.identity.sha1 y "Parent SHA256" a file.parent.identity.sha256. |
|
| timestamp.seconds | Se asigna directamente desde date después de convertirlo en una marca de tiempo. |
¿Necesitas más ayuda? Recibe respuestas de los miembros de la comunidad y de los profesionales de Google SecOps.