Coletar dados de inteligência contra ameaças do Team Cymru Scout

Compatível com:

Este documento explica como ingerir dados de inteligência de ameaças do Team Cymru Scout no Google Security Operations usando o Amazon S3.

Antes de começar

Verifique se você tem os pré-requisitos a seguir:

  • Uma instância do Google SecOps
  • Acesso privilegiado ao locatário Team Cymru Scout
  • Acesso privilegiado à AWS (S3, IAM, Lambda, EventBridge)

Conferir os pré-requisitos do Team Cymru Scout

  1. Faça login na plataforma Scout da Team Cymru (em inglês).
  2. Acesse a Web de chaves de API.
  3. Clique no botão Criar.
  4. Forneça a descrição da chave, se necessário.
  5. Clique no botão Criar chave para gerar a chave de API.
  6. Copie e salve em um local seguro os seguintes detalhes:
    • SCOUT_API_KEY: chave de acesso à API
    • SCOUT_BASE_URL: URL base da API Scout.

Configurar o bucket do AWS S3 e o IAM para o Google SecOps

  1. Crie um bucket do Amazon S3 seguindo este guia do usuário: Como criar um bucket.
  2. Salve o Nome e a Região do bucket para referência futura (por exemplo, team-cymru-scout-ti).
  3. Crie um usuário seguindo este guia: Como criar um usuário do IAM.
  4. Selecione o usuário criado.
  5. Selecione a guia Credenciais de segurança.
  6. Clique em Criar chave de acesso na seção Chaves de acesso.
  7. Selecione Serviço de terceiros como Caso de uso.
  8. Clique em Próxima.
  9. Opcional: adicione uma tag de descrição.
  10. Clique em Criar chave de acesso.
  11. Clique em Fazer o download do arquivo CSV para salvar a chave de acesso e a chave de acesso secreta para referência futura.
  12. Clique em Concluído.
  13. Selecione a guia Permissões.
  14. Clique em Adicionar permissões na seção Políticas de permissões.
  15. Selecione Adicionar permissões.
  16. Selecione Anexar políticas diretamente.
  17. Pesquise a política AmazonS3FullAccess.
  18. Selecione a política.
  19. Clique em Próxima.
  20. Clique em Adicionar permissões

Configurar a política e o papel do IAM para uploads do S3

  1. No console da AWS, acesse IAM > Políticas.
  2. Clique em Criar política > guia JSON.
  3. Insira a seguinte política:

    {
    "Version": "2012-10-17",
    "Statement": [
        {
        "Sid": "AllowPutObjects",
        "Effect": "Allow",
        "Action": "s3:PutObject",
        "Resource": "arn:aws:s3:::team-cymru-scout-ti/*"
        },
        {
        "Sid": "AllowGetStateObject",
        "Effect": "Allow",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::team-cymru-scout-ti/team-cymru/scout-ti/state.json"
        }
    ]
    }
    
    • Substitua team-cymru-scout-ti se você tiver inserido um nome de bucket diferente.
  4. Clique em Próxima > Criar política.

  5. Acesse IAM > Funções > Criar função > Serviço da AWS > Lambda.

  6. Anexe a política recém-criada.

  7. Nomeie a função como TeamCymruScoutToS3Role e clique em Criar função.

Criar a função Lambda

  1. No console da AWS, acesse Lambda > Functions > Create function.
  2. Clique em Criar do zero.
  3. Informe os seguintes detalhes de configuração:

    Configuração Valor
    Nome team_cymru_scout_ti_to_s3
    Ambiente de execução Python 3.13
    Arquitetura x86_64
    Função de execução TeamCymruScoutToS3Role
  4. Depois que a função for criada, abra a guia Código, exclua o stub e insira o seguinte código (team_cymru_scout_ti_to_s3.py):

    ```python
    #!/usr/bin/env python3
    # Lambda: Pull Team Cymru Scout Threat Intelligence exports to S3 (no transform)
    
    import os, json, time
    from urllib.request import Request, urlopen
    from urllib.error import HTTPError, URLError
    import boto3
    
    S3_BUCKET    = os.environ["S3_BUCKET"]
    S3_PREFIX    = os.environ.get("S3_PREFIX", "team-cymru/scout-ti/")
    STATE_KEY    = os.environ.get("STATE_KEY", "team-cymru/scout-ti/state.json")
    WINDOW_SEC   = int(os.environ.get("WINDOW_SECONDS", "3600"))
    HTTP_TIMEOUT = int(os.environ.get("HTTP_TIMEOUT", "60"))
    HTTP_RETRIES = int(os.environ.get("HTTP_RETRIES", "3"))
    MODE         = os.environ.get("MODE", "GET").upper()
    API_HEADERS  = json.loads(os.environ.get("API_HEADERS", "{}"))
    MAX_PAGES    = int(os.environ.get("MAX_PAGES", "10"))
    
    # GET mode
    DOWNLOAD_URL_TEMPLATE = os.environ.get("DOWNLOAD_URL_TEMPLATE", "")
    # POST_JSON mode
    API_URL            = os.environ.get("API_URL", "")
    JSON_BODY_TEMPLATE = os.environ.get("JSON_BODY_TEMPLATE", "")
    
    # Team Cymru Scout specific
    SCOUT_BASE_URL = os.environ.get("SCOUT_BASE_URL", "https://api.scout.cymru.com")
    SCOUT_API_KEY  = os.environ.get("SCOUT_API_KEY", "")
    
    s3 = boto3.client("s3")
    
    def _iso(ts: float) -> str:
        return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(ts))
    
    def _get_state() -> dict:
        try:
            obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY)
            b = obj["Body"].read()
            return json.loads(b) if b else {}
        except Exception:
            return {}
    
    def _put_state(st: dict):
        s3.put_object(
            Bucket=S3_BUCKET, Key=STATE_KEY,
            Body=json.dumps(st, separators=(",", ":")).encode("utf-8"),
            ContentType="application/json",
        )
    
    def _http(url: str, method: str = "GET", body: bytes | None = None) -> tuple[bytes, str]:
        attempt = 0
        while True:
            try:
                req = Request(url, method=method)
                # Add headers
                headers = API_HEADERS.copy()
                if SCOUT_API_KEY and "Authorization" not in headers:
                    headers["Authorization"] = f"Bearer {SCOUT_API_KEY}"
                headers.setdefault("Accept", "application/json")
    
                for k, v in headers.items():
                    req.add_header(k, v)
    
                if body is not None:
                    req.add_header("Content-Type", "application/json")
    
                with urlopen(req, data=body, timeout=HTTP_TIMEOUT) as r:
                    return r.read(), r.headers.get("Content-Type", "application/json")
            except HTTPError as e:
                if e.code in (429, 500, 502, 503, 504) and attempt < HTTP_RETRIES:
                    delay = 1 + attempt
                    try:
                        delay = int(e.headers.get("Retry-After", delay))
                    except Exception:
                        pass
                    time.sleep(max(1, delay))
                    attempt += 1
                    continue
                raise
            except URLError:
                if attempt < HTTP_RETRIES:
                    time.sleep(1 + attempt)
                    attempt += 1
                    continue
                raise
    
    def _write(blob: bytes, ctype: str, from_ts: float, to_ts: float, page: int) -> str:
        date_path = time.strftime("%Y/%m/%d", time.gmtime(to_ts))
        key = f"{S3_PREFIX}/{date_path}/scout_ti_{int(from_ts)}_{int(to_ts)}_p{page:03d}.json"
        s3.put_object(Bucket=S3_BUCKET, Key=key, Body=blob, ContentType=ctype or "application/json")
        return key
    
    def _next_cursor(obj: dict) -> str | None:
        if not isinstance(obj, dict):
            return None
    
        for container in (obj, obj.get("meta", {}) or {}, obj.get("metadata", {}) or {}):
            for k in ("next", "next_cursor", "nextCursor", "nextPageToken", "continuation", "cursor", "pagedResultsCookie"):
                v = container.get(k)
                if v:
                    return str(v)
        return None
    
    def _loop(from_ts: float, to_ts: float) -> dict:
        cursor, page, written = None, 0, 0
    
        while page < MAX_PAGES:
            if MODE == "GET":
                if DOWNLOAD_URL_TEMPLATE:
                    url = (DOWNLOAD_URL_TEMPLATE
                        .replace("{FROM}", _iso(from_ts))
                        .replace("{TO}", _iso(to_ts))
                        .replace("{CURSOR}", cursor or ""))
                else:
                    # Default Scout API endpoint (adjust based on actual API)
                    url = f"{SCOUT_BASE_URL}/v1/threat-intelligence?start={_iso(from_ts)}&end={_iso(to_ts)}"
                    if cursor:
                        url += f"&cursor={cursor}"
                blob, ctype = _http(url, method="GET")
            else:
                assert API_URL and JSON_BODY_TEMPLATE, "API_URL and JSON_BODY_TEMPLATE required for MODE=POST_JSON"
                body = (JSON_BODY_TEMPLATE
                        .replace("{FROM}", _iso(from_ts))
                        .replace("{TO}", _iso(to_ts))
                        .replace("{CURSOR}", cursor or "")).encode("utf-8")
                blob, ctype = _http(API_URL, method="POST", body=body)
    
            # Normalize to JSON bytes for storage
            try:
                parsed = json.loads(blob.decode("utf-8"))
                normalized = json.dumps(parsed, separators=(",", ":")).encode("utf-8")
                ctype_out = "application/json"
            except Exception:
                normalized = blob
                ctype_out = ctype or "application/octet-stream"
    
            _ = _write(normalized, ctype_out, from_ts, to_ts, page)
            written += 1
            page += 1
    
            # Follow cursor if JSON and cursor exists
            try:
                if parsed and isinstance(parsed, dict):
                    cursor = _next_cursor(parsed)
                if not cursor:
                    break
            except Exception:
                break
    
        return {"pages": page, "objects": written}
    
    def lambda_handler(event=None, context=None):
        st = _get_state()
        now = time.time()
        from_ts = st.get("last_to_ts") or (now - WINDOW_SEC)
        to_ts = now
        res = _loop(from_ts, to_ts)
        st["last_to_ts"] = to_ts
        _put_state(st)
        return {"ok": True, "window": {"from": _iso(from_ts), "to": _iso(to_ts)}, **res}
    
    if __name__ == "__main__":
        print(lambda_handler())
    ```
    
  5. Acesse Configuração > Variáveis de ambiente.

  6. Clique em Editar > Adicionar nova variável de ambiente.

  7. Insira as variáveis de ambiente a seguir, substituindo pelos seus valores.

    Chave Valor de exemplo
    S3_BUCKET team-cymru-scout-ti
    S3_PREFIX team-cymru/scout-ti/
    STATE_KEY team-cymru/scout-ti/state.json
    SCOUT_BASE_URL https://api.scout.cymru.com
    SCOUT_API_KEY your-scout-api-key
    WINDOW_SECONDS 3600
    HTTP_TIMEOUT 60
    HTTP_RETRIES 3
    MODE GET ou POST_JSON
    API_HEADERS {"Authorization":"Bearer <token>","Accept":"application/json"}
    DOWNLOAD_URL_TEMPLATE (Modo GET) Modelo de URL personalizado com {FROM}, {TO}, {CURSOR}
    API_URL URL do endpoint de API (modo POST_JSON)
    JSON_BODY_TEMPLATE (modo POST_JSON) Corpo JSON com {FROM}, {TO}, {CURSOR}
    MAX_PAGES 10
  8. Depois que a função for criada, permaneça na página dela ou abra Lambda > Functions > sua-função.

  9. Selecione a guia Configuração.

  10. No painel Configuração geral, clique em Editar.

  11. Mude Tempo limite para 5 minutos (300 segundos) e clique em Salvar.

Criar uma programação do EventBridge

  1. Acesse Amazon EventBridge > Scheduler > Criar programação.
  2. Informe os seguintes detalhes de configuração:
    • Programação recorrente: Taxa (1 hour).
    • Destino: sua função Lambda team_cymru_scout_ti_to_s3.
    • Nome: team-cymru-scout-ti-1h.
  3. Clique em Criar programação.

Opcional: criar um usuário e chaves do IAM somente leitura para o Google SecOps

  1. Acesse Console da AWS > IAM > Usuários > Adicionar usuários.
  2. Clique em Add users.
  3. Informe os seguintes detalhes de configuração:
    • Usuário: insira secops-reader.
    • Tipo de acesso: selecione Chave de acesso – Acesso programático.
  4. Clique em Criar usuário.
  5. 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.
  6. No editor JSON, insira a seguinte política:

    {
    "Version": "2012-10-17",
    "Statement": [
        {
        "Effect": "Allow",
        "Action": ["s3:GetObject"],
        "Resource": "arn:aws:s3:::team-cymru-scout-ti/*"
        },
        {
        "Effect": "Allow",
        "Action": ["s3:ListBucket"],
        "Resource": "arn:aws:s3:::team-cymru-scout-ti"
        }
    ]
    }
    
  7. Defina o nome como secops-reader-policy.

  8. Acesse Criar política > pesquise/selecione > Próxima > Adicionar permissões.

  9. Acesse Credenciais de segurança > Chaves de acesso > Criar chave de acesso.

  10. Faça o download do CSV (esses valores são inseridos no feed).

Configurar um feed no Google SecOps para ingerir a inteligência de ameaças do Team Cymru Scout

  1. Acesse Configurações do SIEM > Feeds.
  2. Clique em Adicionar novo feed.
  3. No campo Nome do feed, insira um nome para o feed (por exemplo, Team Cymru Scout Threat Intelligence).
  4. Selecione Amazon S3 V2 como o Tipo de origem.
  5. Selecione Team Cymru Scout Threat Intelligence como o Tipo de registro.
  6. Clique em Próxima.
  7. Especifique valores para os seguintes parâmetros de entrada:
    • URI do S3: s3://team-cymru-scout-ti/team-cymru/scout-ti/
    • Opções de exclusão da fonte: 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.
  8. Clique em Próxima.
  9. Revise a nova configuração do feed na tela Finalizar e clique em Enviar.

Formatos de registro de inteligência de ameaças do Team Cymru Scout compatíveis

O analisador de inteligência de ameaças do Team Cymru Scout é compatível com registros nos formatos KV (LEEF) e CSV.

Registros de amostra de inteligência contra ameaças do Team Cymru Scout compatíveis

  • JSON

    {
      "account_name": "dummy_secops_user",
      "account_type": "basic_auth",
      "used_queries": 1414,
      "remaining_queries": 48586,
      "used_queries_percentage": 2.828,
      "query_limit": 50000,
      "used_foundation_queries": 4224,
      "remaining_foundation_queries": 5776,
      "foundation_query_limit": 10000,
      "used_foundation_queries_percentage": 42.24,
      "event_type": "account_usage"
    }
    

Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais do Google SecOps.