Recolha dados de informações sobre ameaças do Team Cymru Scout

Suportado em:

Este documento explica como carregar dados de informações sobre ameaças do Team Cymru Scout para o Google Security Operations através do Amazon S3.

Antes de começar

Certifique-se de que cumpre os seguintes pré-requisitos:

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

Obtenha os pré-requisitos do Team Cymru Scout

  1. Inicie sessão na plataforma Team Cymru Scout.
  2. Aceda à Web de chaves da API.
  3. Clique no botão Criar.
  4. Se necessário, indique a descrição da chave.
  5. Clique no botão Criar chave para gerar a chave da API.
  6. Copie e guarde numa localização segura os seguintes detalhes:
    • SCOUT_API_KEY: chave de acesso à API
    • SCOUT_BASE_URL: URL base da API Scout

Configure o contentor do AWS S3 e o IAM para o Google SecOps

  1. Crie um contentor do Amazon S3 seguindo este manual do utilizador: Criar um contentor.
  2. Guarde o nome e a região do contentor para referência futura (por exemplo, team-cymru-scout-ti).
  3. Crie um utilizador seguindo este guia do utilizador: criar um utilizador do IAM.
  4. Selecione o utilizador criado.
  5. Selecione o separador Credenciais de segurança.
  6. Clique em Criar chave de acesso na secção Chaves de acesso.
  7. Selecione Serviço de terceiros como Exemplo de utilização.
  8. Clicar em Seguinte.
  9. Opcional: adicione uma etiqueta de descrição.
  10. Clique em Criar chave de acesso.
  11. Clique em Transferir ficheiro CSV para guardar a chave de acesso e a chave de acesso secreta para referência futura.
  12. Clique em Concluído.
  13. Selecione o separador Autorizações.
  14. Clique em Adicionar autorizações na secção Políticas de autorizações.
  15. Selecione Adicionar autorizações.
  16. Selecione Anexar políticas diretamente.
  17. Pesquise a política AmazonS3FullAccess.
  18. Selecione a política.
  19. Clicar em Seguinte.
  20. Clique em Adicionar autorizações.

Configure a política e a função de IAM para carregamentos do S3

  1. Na consola da AWS, aceda a IAM > Políticas.
  2. Clique em Criar política > separador JSON.
  3. Introduza 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 tiver introduzido um nome de contentor diferente.
  4. Clique em Seguinte > Criar política.

  5. Aceda a IAM > Funções > Criar função > Serviço AWS > Lambda.

  6. Anexe a política criada recentemente.

  7. Dê o nome TeamCymruScoutToS3Role à função e clique em Criar função.

Crie a função Lambda

  1. Na consola da AWS, aceda a Lambda > Functions > Create function.
  2. Clique em Criar do zero.
  3. Faculte os seguintes detalhes de configuração:

    Definição Valor
    Nome team_cymru_scout_ti_to_s3
    Runtime Python 3.13
    Arquitetura x86_64
    Função de execução TeamCymruScoutToS3Role
  4. Depois de criar a função, abra o separador Código, elimine o fragmento de código e introduza 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. Aceda a Configuração > Variáveis de ambiente.

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

  7. Introduza as seguintes variáveis de ambiente fornecidas, substituindo-as pelos seus valores.

    Chave Exemplo de valor
    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} e {CURSOR}
    API_URL URL do ponto final da API (modo POST_JSON)
    JSON_BODY_TEMPLATE (POST_JSON mode) Corpo JSON com {FROM}, {TO}, {CURSOR}
    MAX_PAGES 10
  8. Depois de criar a função, permaneça na respetiva página (ou abra Lambda > Functions > your-function).

  9. Selecione o separador Configuração.

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

  11. Altere Tempo limite para 5 minutos (300 segundos) e clique em Guardar.

Crie um horário do EventBridge

  1. Aceda a Amazon EventBridge > Scheduler > Create schedule.
  2. Forneça os seguintes detalhes de configuração:
    • Programação recorrente: Taxa (1 hour).
    • Destino: a sua função Lambda team_cymru_scout_ti_to_s3.
    • Nome: team-cymru-scout-ti-1h.
  3. Clique em Criar horário.

Opcional: crie um utilizador e chaves da IAM só de leitura para o Google SecOps

  1. Aceda a AWS Console > IAM > Users > Add users.
  2. Clique em Adicionar utilizadores.
  3. Forneça os seguintes detalhes de configuração:
    • Utilizador: introduza secops-reader.
    • Tipo de acesso: selecione Chave de acesso – Acesso programático.
  4. Clique em Criar utilizador.
  5. Anexe a política de leitura mínima (personalizada): Users > secops-reader > Permissions > Add permissions > Attach policies directly > Create policy.
  6. No editor JSON, introduza 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. Aceda a Criar política > pesquise/selecione > Seguinte > Adicionar autorizações.

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

  10. Transfira o CSV (estes valores são introduzidos no feed).

Configure um feed no Google SecOps para carregar informações sobre ameaças do Team Cymru Scout

  1. Aceda a Definições do SIEM > Feeds.
  2. Clique em Adicionar novo feed.
  3. No campo Nome do feed, introduza 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 registo.
  6. Clicar em Seguinte.
  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 eliminação de origens: selecione a opção de eliminação de acordo com a sua preferência.
    • Idade máxima do ficheiro: inclua ficheiros modificados no último número de dias. A predefinição é 180 dias.
    • ID da chave de acesso: chave de acesso do utilizador com acesso ao contentor do S3.
    • Chave de acesso secreta: chave secreta do utilizador com acesso ao contentor do S3.
    • Espaço de nomes do recurso: o espaço de nomes do recurso.
    • Etiquetas de carregamento: a etiqueta aplicada aos eventos deste feed.
  8. Clicar em Seguinte.
  9. Reveja a nova configuração do feed no ecrã Finalizar e, de seguida, clique em Enviar.

Formatos de registos de informações sobre ameaças do Team Cymru Scout suportados

O analisador de informações sobre ameaças do Team Cymru Scout suporta registos nos formatos KV (LEEF) e CSV.

Registos de exemplo de informações sobre ameaças do Team Cymru Scout suportados

  • 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 da Google SecOps.