Coletar registros do TeamViewer
Este documento explica como ingerir registros do TeamViewer no Google Security Operations usando o Amazon S3. O analisador extrai os eventos de auditoria de registros formatados em JSON. Ele itera pelos detalhes do evento, mapeando propriedades específicas para campos do modelo de dados unificado (UDM, na sigla em inglês), processando informações de participantes e apresentadores e categorizando eventos com base na atividade do usuário. O analisador também realiza transformações de dados, como mesclar rótulos e converter carimbos de data/hora em um formato padronizado.
Antes de começar
Verifique se você tem os pré-requisitos a seguir:
- Uma instância do Google SecOps.
- Acesso privilegiado ao TeamViewer.
- Acesso privilegiado à AWS (S3, Identity and Access Management (IAM), Lambda, EventBridge).
Conferir os pré-requisitos do TeamViewer
- Faça login no TeamViewer Management Console como administrador.
- Acesse Meu perfil > Apps.
- Clique em Criar App.
- Informe os seguintes detalhes de configuração:
- Nome do app: insira um nome descritivo, por exemplo,
Google SecOps Integration
. - Descrição: insira uma descrição para o app.
- Permissões: selecione as permissões de acesso ao registro de auditoria.
- Nome do app: insira um nome descritivo, por exemplo,
- Clique em Criar e salve as credenciais de API geradas em um local seguro.
- Registre o URL base da API TeamViewer (por exemplo,
https://webapi.teamviewer.com/api/v1
). - Copie e salve em um local seguro os seguintes detalhes:
- CLIENT_ID
- CLIENT_SECRET
- API_BASE_URL
Configurar o bucket do AWS S3 e o IAM para o Google SecOps
- Crie um bucket do Amazon S3 seguindo este guia do usuário: Como criar um bucket
- Salve o Nome e a Região do bucket para referência futura (por exemplo,
teamviewer-logs
). - Crie um usuário seguindo este guia: Como criar um usuário do IAM.
- Selecione o usuário criado.
- Selecione a guia Credenciais de segurança.
- Clique em Criar chave de acesso na seção Chaves de acesso.
- Selecione Serviço de terceiros como Caso de uso.
- Clique em Próxima.
- Opcional: adicione uma tag de descrição.
- Clique em Criar chave de acesso.
- Clique em Fazer o download do arquivo CSV para salvar a chave de acesso e a chave de acesso secreta para referência futura.
- Clique em Concluído.
- Selecione a guia Permissões.
- Clique em Adicionar permissões na seção Políticas de permissões.
- Selecione Adicionar permissões.
- Selecione Anexar políticas diretamente.
- Pesquise a política AmazonS3FullAccess.
- Selecione a política.
- Clique em Próxima.
- Clique em Adicionar permissões
Configurar a política e o papel do IAM para uploads do S3
- No console da AWS, acesse IAM > Políticas.
- Clique em Criar política > guia JSON.
- Copie e cole a política a seguir.
JSON da política (substitua
teamviewer-logs
se você tiver inserido um nome de bucket diferente):{ "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" } ] }
Clique em Próxima > Criar política.
Acesse IAM > Funções > Criar função > Serviço da AWS > Lambda.
Anexe a política recém-criada.
Nomeie a função como
TeamViewerToS3Role
e clique em Criar função.
Criar a função Lambda
- No console da AWS, acesse Lambda > Functions > Create function.
- Clique em Criar do zero.
Informe os seguintes detalhes de configuração:
Configuração Valor Nome teamviewer_to_s3
Ambiente de execução Python 3.13 Arquitetura x86_64 Função de execução TeamViewerToS3Role
Depois que a função for criada, abra a guia Código, exclua o stub e cole o código a seguir (
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())
Acesse Configuração > Variáveis de ambiente.
Clique em Editar > Adicionar nova variável de ambiente.
Insira as variáveis de ambiente fornecidas na tabela a seguir, substituindo os valores de exemplo pelos seus.
Variáveis de ambiente
Chave Valor de exemplo 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
(da etapa 2)CLIENT_SECRET
your-client-secret
(da etapa 2)Depois que a função for criada, permaneça na página dela ou abra Lambda > Functions > sua-função.
Selecione a guia Configuração.
No painel Configuração geral, clique em Editar.
Mude Tempo limite para 5 minutos (300 segundos) e clique em Salvar.
Criar uma programação do EventBridge
- Acesse Amazon EventBridge > Scheduler > Criar programação.
- Informe os seguintes detalhes de configuração:
- Programação recorrente: Taxa (
1 hour
). - Destino: sua função Lambda
teamviewer_to_s3
. - Nome:
teamviewer-audit-1h
.
- Programação recorrente: Taxa (
- Clique em Criar programação.
(Opcional) Criar um usuário e chaves do IAM somente leitura para o Google SecOps
- Acesse Console da AWS > IAM > Usuários > Adicionar usuários.
- Clique em Add users.
- Informe os seguintes detalhes de configuração:
- Usuário: insira
secops-reader
. - Tipo de acesso: selecione Chave de acesso – Acesso programático.
- Usuário: insira
- Clique em Criar usuário.
- Anexe a política de leitura mínima (personalizada): Usuários > secops-reader > Permissões > Adicionar permissões > Anexar políticas diretamente > Criar política.
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" } ] }
Name =
secops-reader-policy
.Clique em Criar política > pesquisar/selecionar > Próxima > Adicionar permissões.
Crie uma chave de acesso para
secops-reader
: Credenciais de segurança > Chaves de acesso.Clique em Criar chave de acesso.
Faça o download do
CSV
. Cole esses valores no feed.
Configurar um feed no Google SecOps para ingerir registros do TeamViewer
- Acesse Configurações do SIEM > Feeds.
- Clique em + Adicionar novo feed.
- No campo Nome do feed, insira um nome para o feed (por exemplo,
TeamViewer logs
). - Selecione Amazon S3 V2 como o Tipo de origem.
- Selecione TeamViewer como o Tipo de registro.
- Clique em Próxima.
- Especifique valores para os seguintes parâmetros de entrada:
- URI do S3:
s3://teamviewer-logs/teamviewer/audit/
- Opções de exclusão de fontes: selecione a opção de exclusão de acordo com sua preferência.
- Idade máxima do arquivo: inclui arquivos modificados no último número de dias. O padrão é de 180 dias.
- ID da chave de acesso: chave de acesso do usuário com acesso ao bucket do S3.
- Chave de acesso secreta: chave secreta do usuário com acesso ao bucket do S3.
- Namespace do recurso: o namespace do recurso.
- Rótulos de ingestão: o rótulo aplicado aos eventos deste feed.
- URI do S3:
- Clique em Próxima.
- Revise a nova configuração do feed na tela Finalizar e clique em Enviar.
Tabela de mapeamento do UDM
Campo de registro | Mapeamento do UDM | Lógica |
---|---|---|
AffectedItem |
metadata.product_log_id |
O valor de AffectedItem do registro bruto é mapeado diretamente para esse campo do UDM. |
EventDetails.NewValue |
principal.resource.attribute.labels.value |
Se PropertyName contiver (server) , NewValue será usado como o valor de um rótulo em principal.resource.attribute.labels . |
EventDetails.NewValue |
principal.user.user_display_name |
Se PropertyName for Name of participant , o NewValue será usado como o nome de exibição do usuário para o principal. |
EventDetails.NewValue |
principal.user.userid |
Se PropertyName for ID of participant , o NewValue será usado como o ID do usuário para o principal. |
EventDetails.NewValue |
security_result.about.labels.value |
Para todos os outros valores de PropertyName (exceto aqueles processados por condições específicas), o NewValue é usado como o valor de um rótulo na matriz security_result.about.labels . |
EventDetails.NewValue |
target.file.full_path |
Se PropertyName for Source file , o NewValue será usado como o caminho completo para o arquivo de destino. |
EventDetails.NewValue |
target.resource.attribute.labels.value |
Se PropertyName contiver (client) , NewValue será usado como o valor de um rótulo em target.resource.attribute.labels . |
EventDetails.NewValue |
target.user.user_display_name |
Se PropertyName for Name of presenter , o NewValue será analisado. Se for um número inteiro, ele será descartado. Caso contrário, ele será usado como o nome de exibição do usuário para a meta. |
EventDetails.NewValue |
target.user.userid |
Se PropertyName for ID of presenter , o NewValue será usado como o ID do usuário para a meta. |
EventDetails.PropertyName |
principal.resource.attribute.labels.key |
Se PropertyName contiver (server) , PropertyName será usado como a chave de um rótulo em principal.resource.attribute.labels . |
EventDetails.PropertyName |
security_result.about.labels.key |
Para todos os outros valores de PropertyName (exceto aqueles processados por condições específicas), o PropertyName é usado como a chave de um rótulo na matriz security_result.about.labels . |
EventDetails.PropertyName |
target.resource.attribute.labels.key |
Se PropertyName contiver (client) , PropertyName será usado como a chave de um rótulo em target.resource.attribute.labels . |
EventName |
metadata.product_event_type |
O valor de EventName do registro bruto é mapeado diretamente para esse campo do UDM. |
Timestamp |
metadata.event_timestamp |
O valor de Timestamp do registro bruto é analisado e usado como o carimbo de data/hora do evento nos metadados. Defina como USER_UNCATEGORIZED se src_user (derivado de ID of participant ) não estiver vazio. Caso contrário, defina como USER_RESOURCE_ACCESS . Fixado no código como TEAMVIEWER . Fixado no código como TEAMVIEWER . Fixado no código como TEAMVIEWER . |
Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais do Google SecOps.