Recopilar registros de TeamViewer

Disponible en:

En este documento se explica cómo ingerir registros de TeamViewer en Google Security Operations mediante Amazon S3. El analizador extrae los eventos de auditoría de los registros en formato JSON. Recorre los detalles de los eventos, asigna propiedades específicas a los campos del modelo de datos unificado (UDM), gestiona la información de los participantes y los presentadores, y clasifica los eventos en función de la actividad del usuario. El analizador también realiza transformaciones de datos, como combinar etiquetas y convertir marcas de tiempo a un formato estandarizado.

Antes de empezar

Asegúrate de que cumples los siguientes requisitos previos:

  • Una instancia de Google SecOps.
  • Acceso privilegiado a TeamViewer.
  • Acceso privilegiado a AWS (S3, Gestión de Identidades y Accesos [IAM], Lambda y EventBridge).

Requisitos previos de TeamViewer

  1. Inicia sesión en la consola de gestión de TeamViewer como administrador.
  2. Ve a Mi perfil > Aplicaciones.
  3. Haz clic en Crear aplicación.
  4. Proporcione los siguientes detalles de configuración:
    • Nombre de la aplicación: introduce un nombre descriptivo (por ejemplo, Google SecOps Integration).
    • Descripción: introduce una descripción de la aplicación.
    • Permisos: selecciona los permisos de acceso al registro de auditoría.
  5. Haz clic en Crear y guarda las credenciales de la API generadas en una ubicación segura.
  6. Anota la URL base de la API de TeamViewer (por ejemplo, https://webapi.teamviewer.com/api/v1).
  7. Copia y guarda en un lugar seguro los siguientes detalles:
    • CLIENT_ID
    • CLIENT_SECRET
    • API_BASE_URL

Configurar un segmento de AWS S3 y IAM para Google SecOps

  1. Crea un segmento de Amazon S3 siguiendo esta guía de usuario: Crear un segmento.
  2. Guarda el nombre y la región del segmento para consultarlos más adelante (por ejemplo, teamviewer-logs).
  3. Crea un usuario siguiendo esta guía: Crear un usuario de gestión de identidades y accesos.
  4. Selecciona el Usuario creado.
  5. Selecciona la pestaña Credenciales de seguridad.
  6. En la sección Claves de acceso, haz clic en Crear clave de acceso.
  7. Selecciona Servicio de terceros en Caso práctico.
  8. Haz clic en Siguiente.
  9. Opcional: añade una etiqueta de descripción.
  10. Haz clic en Crear clave de acceso.
  11. Haz clic en Descargar archivo CSV para guardar la clave de acceso y la clave de acceso secreta para futuras consultas.
  12. Haz clic en Listo.
  13. Selecciona la pestaña Permisos.
  14. En la sección Políticas de permisos, haz clic en Añadir permisos.
  15. Selecciona Añadir permisos.
  16. Seleccione Adjuntar políticas directamente.
  17. Busca la política AmazonS3FullAccess.
  18. Selecciona la política.
  19. Haz clic en Siguiente.
  20. 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

  1. En la consola de AWS, ve a IAM > Políticas.
  2. Haz clic en Crear política > pestaña JSON.
  3. Copia y pega la siguiente política.
  4. JSON de la política (sustituye teamviewer-logs si has introducido otro nombre de contenedor):

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutObjects",
          "Effect": "Allow",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::teamviewer-logs/*"
        },
        {
          "Sid": "AllowGetStateObject",
          "Effect": "Allow",
          "Action": "s3:GetObject",
          "Resource": "arn:aws:s3:::teamviewer-logs/teamviewer/audit/state.json"
        }
      ]
    }
    
  5. Haz clic en Siguiente > Crear política.

  6. Ve a IAM > Roles > Crear rol > Servicio de AWS > Lambda.

  7. Adjunte la política que acaba de crear.

  8. Dale el nombre TeamViewerToS3Role al rol y haz clic en Crear rol.

Crear la función Lambda

  1. En la consola de AWS, ve a Lambda > Funciones > Crear función.
  2. Haz clic en Crear desde cero.
  3. Proporciona los siguientes detalles de configuración:

    Ajuste Valor
    Nombre teamviewer_to_s3
    Tiempo de ejecución Python 3.13
    Arquitectura x86_64
    Rol de ejecución TeamViewerToS3Role
  4. Una vez creada la función, abra la pestaña Código, elimine el stub y pegue el siguiente código (teamviewer_to_s3.py).

    #!/usr/bin/env python3
    # Lambda: Pull TeamViewer audit logs and store raw JSON payloads to S3
    # - Time window via {FROM}/{TO} placeholders (UTC ISO8601), URL-encoded.
    # - Preserves vendor-native JSON format for audit and session data.
    # - Retries with exponential backoff; unique S3 keys to avoid overwrites.
    
    import os, json, time, uuid, urllib.parse
    from urllib.request import Request, urlopen
    from urllib.error import URLError, HTTPError
    
    import boto3
    
    S3_BUCKET   = os.environ["S3_BUCKET"]
    S3_PREFIX   = os.environ.get("S3_PREFIX", "teamviewer/audit/")
    STATE_KEY   = os.environ.get("STATE_KEY", "teamviewer/audit/state.json")
    WINDOW_SEC  = int(os.environ.get("WINDOW_SECONDS", "3600"))  # default 1h
    HTTP_TIMEOUT= int(os.environ.get("HTTP_TIMEOUT", "60"))
    API_BASE_URL = os.environ["API_BASE_URL"]
    CLIENT_ID   = os.environ["CLIENT_ID"]
    CLIENT_SECRET = os.environ["CLIENT_SECRET"]
    MAX_RETRIES = int(os.environ.get("MAX_RETRIES", "3"))
    USER_AGENT  = os.environ.get("USER_AGENT", "teamviewer-to-s3/1.0")
    
    s3 = boto3.client("s3")
    
    def _load_state():
        try:
            obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY)
            return json.loads(obj["Body"].read())
        except Exception:
            return {}
    
    def _save_state(st):
        s3.put_object(
            Bucket=S3_BUCKET,
            Key=STATE_KEY,
            Body=json.dumps(st, separators=(",", ":")).encode("utf-8"),
            ContentType="application/json",
        )
    
    def _iso(ts: float) -> str:
        return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(ts))
    
    def _get_access_token() -> str:
        # OAuth2 Client Credentials flow for TeamViewer API
        token_url = f"{API_BASE_URL.rstrip('/')}/oauth2/token"
        data = urllib.parse.urlencode({
            'grant_type': 'client_credentials',
            'client_id': CLIENT_ID,
            'client_secret': CLIENT_SECRET
        }).encode('utf-8')
    
        req = Request(token_url, data=data, method="POST")
        req.add_header("Content-Type", "application/x-www-form-urlencoded")
        req.add_header("User-Agent", USER_AGENT)
    
        with urlopen(req, timeout=HTTP_TIMEOUT) as r:
            response = json.loads(r.read())
            return response["access_token"]
    
    def _build_audit_url(from_ts: float, to_ts: float, access_token: str) -> str:
        # Build URL for TeamViewer audit API endpoint
        base_endpoint = f"{API_BASE_URL.rstrip('/')}/reports/connections"
        params = {
            "from_date": _iso(from_ts),
            "to_date": _iso(to_ts)
        }
        query_string = urllib.parse.urlencode(params)
        return f"{base_endpoint}?{query_string}"
    
    def _fetch_audit_data(url: str, access_token: str) -> tuple[bytes, str]:
        attempt = 0
        while True:
            req = Request(url, method="GET")
            req.add_header("User-Agent", USER_AGENT)
            req.add_header("Authorization", f"Bearer {access_token}")
            req.add_header("Accept", "application/json")
    
            try:
                with urlopen(req, timeout=HTTP_TIMEOUT) as r:
                    return r.read(), (r.headers.get("Content-Type") or "application/json")
            except (HTTPError, URLError) as e:
                attempt += 1
                print(f"HTTP error on attempt {attempt}: {e}")
                if attempt > MAX_RETRIES:
                    raise
                # exponential backoff with jitter
                time.sleep(min(60, 2 ** attempt) + (time.time() % 1))
    
    def _put_audit_data(blob: bytes, content_type: str, from_ts: float, to_ts: float) -> str:
        # Create unique S3 key for audit data
        ts_path = time.strftime("%Y/%m/%d", time.gmtime(to_ts))
        uniq = f"{int(time.time()*1e6)}_{uuid.uuid4().hex[:8]}"
        key = f"{S3_PREFIX}{ts_path}/teamviewer_audit_{int(from_ts)}_{int(to_ts)}_{uniq}.json"
    
        s3.put_object(
            Bucket=S3_BUCKET, 
            Key=key, 
            Body=blob, 
            ContentType=content_type,
            Metadata={
                'source': 'teamviewer-audit',
                'from_timestamp': str(int(from_ts)),
                'to_timestamp': str(int(to_ts))
            }
        )
        return key
    
    def lambda_handler(event=None, context=None):
        st = _load_state()
        now = time.time()
        from_ts = float(st.get("last_to_ts") or (now - WINDOW_SEC))
        to_ts = now
    
        # Get OAuth2 access token
        access_token = _get_access_token()
    
        url = _build_audit_url(from_ts, to_ts, access_token)
        print(f"Fetching TeamViewer audit data from: {url}")
    
        blob, ctype = _fetch_audit_data(url, access_token)
    
        # Validate that we received valid JSON data
        try:
            audit_data = json.loads(blob)
            print(f"Successfully retrieved {len(audit_data.get('records', []))} audit records")
        except json.JSONDecodeError as e:
            print(f"Warning: Invalid JSON received: {e}")
    
        key = _put_audit_data(blob, ctype, from_ts, to_ts)
    
        st["last_to_ts"] = to_ts
        st["last_successful_run"] = now
        _save_state(st)
    
        return {
            "statusCode": 200,
            "body": {
                "success": True, 
                "s3_key": key, 
                "content_type": ctype,
                "from_timestamp": from_ts,
                "to_timestamp": to_ts
            }
        }
    
    if __name__ == "__main__":
        print(lambda_handler())
    
  5. Vaya a Configuración > Variables de entorno.

  6. Haz clic en Editar > Añadir nueva variable de entorno.

  7. Introduce las variables de entorno que se indican en la siguiente tabla y sustituye los valores de ejemplo por los tuyos.

    Variables de entorno

    Clave Valor de ejemplo
    S3_BUCKET teamviewer-logs
    S3_PREFIX teamviewer/audit/
    STATE_KEY teamviewer/audit/state.json
    WINDOW_SECONDS 3600
    HTTP_TIMEOUT 60
    MAX_RETRIES 3
    USER_AGENT teamviewer-to-s3/1.0
    API_BASE_URL https://webapi.teamviewer.com/api/v1
    CLIENT_ID your-client-id (desde el paso 2)
    CLIENT_SECRET your-client-secret (desde el paso 2)
  8. Una vez creada la función, permanece en su página (o abre Lambda > Funciones > tu-función).

  9. Seleccione la pestaña Configuración.

  10. En el panel Configuración general, haz clic en Editar.

  11. Cambia Tiempo de espera a 5 minutos (300 segundos) y haz clic en Guardar.

Crear una programación de EventBridge

  1. Ve a Amazon EventBridge > Scheduler > Create schedule (Amazon EventBridge > Programador > Crear programación).
  2. Proporcione los siguientes detalles de configuración:
    • Programación periódica: Precio (1 hour).
    • Destino: tu función Lambda teamviewer_to_s3.
    • Nombre: teamviewer-audit-1h.
  3. Haz clic en Crear programación.

(Opcional) Crear un usuario y claves de IAM de solo lectura para Google SecOps

  1. Ve a Consola de AWS > IAM > Usuarios > Añadir usuarios.
  2. Haz clic en Add users (Añadir usuarios).
  3. Proporcione los siguientes detalles de configuración:
    • Usuario: introduce secops-reader.
    • Tipo de acceso: selecciona Clave de acceso – Acceso programático.
  4. Haz clic en Crear usuario.
  5. Asigna una política de lectura mínima (personalizada): Usuarios > secops-reader > Permisos > Añadir permisos > Asignar políticas directamente > Crear política.
  6. JSON:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["s3:GetObject"],
          "Resource": "arn:aws:s3:::teamviewer-logs/*"
        },
        {
          "Effect": "Allow",
          "Action": ["s3:ListBucket"],
          "Resource": "arn:aws:s3:::teamviewer-logs"
        }
      ]
    }
    
  7. Nombre = secops-reader-policy.

  8. Haz clic en Crear política > busca o selecciona > Siguiente > Añadir permisos.

  9. Crea una clave de acceso para secops-reader: Credenciales de seguridad > Claves de acceso.

  10. Haz clic en Crear clave de acceso.

  11. Descarga la CSV. (Estos valores se pegarán en el feed).

Configurar un feed en Google SecOps para ingerir registros de TeamViewer

  1. Ve a Configuración de SIEM > Feeds.
  2. Haz clic en + Añadir nuevo feed.
  3. En el campo Nombre del feed, introduce un nombre para el feed (por ejemplo, TeamViewer logs).
  4. Selecciona Amazon S3 V2 como Tipo de fuente.
  5. Seleccione TeamViewer como Tipo de registro.
  6. Haz clic en Siguiente.
  7. Especifique los valores de los siguientes parámetros de entrada:
    • URI de S3: s3://teamviewer-logs/teamviewer/audit/
    • 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.
  8. Haz clic en Siguiente.
  9. 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
AffectedItem metadata.product_log_id El valor de AffectedItem del registro sin procesar se asigna directamente a este campo de UDM.
EventDetails.NewValue principal.resource.attribute.labels.value Si PropertyName contiene (server), se usa NewValue como valor de una etiqueta en principal.resource.attribute.labels.
EventDetails.NewValue principal.user.user_display_name Si PropertyName es Name of participant, se usa NewValue como nombre visible del usuario para la entidad.
EventDetails.NewValue principal.user.userid Si PropertyName es ID of participant, se usa NewValue como ID de usuario de la entidad principal.
EventDetails.NewValue security_result.about.labels.value Para todos los demás valores de PropertyName (excepto los que se gestionan mediante condiciones específicas), se usa NewValue como valor de una etiqueta del array security_result.about.labels.
EventDetails.NewValue target.file.full_path Si PropertyName es Source file, NewValue se usa como ruta completa del archivo de destino.
EventDetails.NewValue target.resource.attribute.labels.value Si PropertyName contiene (client), se usa NewValue como valor de una etiqueta en target.resource.attribute.labels.
EventDetails.NewValue target.user.user_display_name Si PropertyName es Name of presenter, se analiza NewValue. Si es un entero, se descarta. De lo contrario, se usa como nombre visible del usuario para el destino.
EventDetails.NewValue target.user.userid Si PropertyName es ID of presenter, se usa NewValue como ID de usuario del objetivo.
EventDetails.PropertyName principal.resource.attribute.labels.key Si PropertyName contiene (server), PropertyName se usa como clave de una etiqueta en principal.resource.attribute.labels.
EventDetails.PropertyName security_result.about.labels.key Para todos los demás valores de PropertyName (excepto los que se gestionan mediante condiciones específicas), PropertyName se usa como clave de una etiqueta en la matriz security_result.about.labels.
EventDetails.PropertyName target.resource.attribute.labels.key Si PropertyName contiene (client), PropertyName se usa como clave de una etiqueta en target.resource.attribute.labels.
EventName metadata.product_event_type El valor de EventName del registro sin procesar se asigna directamente a este campo de UDM.
Timestamp metadata.event_timestamp El valor de Timestamp del registro sin procesar se analiza y se usa como la marca de tiempo del evento en los metadatos. Se le asigna el valor USER_UNCATEGORIZED si src_user (derivado de ID of participant) no está vacío. De lo contrario, se le asigna el valor USER_RESOURCE_ACCESS. Valor fijo establecido en el código fuente TEAMVIEWER. Valor fijo establecido en el código fuente TEAMVIEWER. Valor fijo establecido en el código fuente TEAMVIEWER.

¿Necesitas más ayuda? Recibe respuestas de los miembros de la comunidad y de los profesionales de Google SecOps.