# Como as Transmissões Piratas da Copa do Mundo Realmente Funcionam


Eu quis descobrir como aquele truque escondido à plena vista realmente funcionava. Sites de streaming pirata existem desde sempre, mas durante a Copa do Mundo você não conseguia abrir um fórum de esportes sem alguém soltando um link para uma partida ao vivo — Full HD, sem buffering, sem login. Eu sempre presumi que havia algo exótico acontecendo por baixo dos panos. Um amigo me mandou um link antes do jogo Inglaterra-Croácia. "Assiste isso", ele disse. Então eu abri o DevTools.

Eu esperava uma playlist HLS raspada ou um feed retransmitido. Em vez disso, encontrei uma tag `<video>`, uma instância do Shaka Player apontando para um CDN legítimo da Akamai, e duas strings hexadecimais em um bloco `<script>` que faziam todo o trabalho. Aquelas strings hexadecimais eram as chaves de descriptografia do DRM. No código-fonte da página. Não atrás de uma API. Não criptografadas. Apenas envoltas em JavaScript que parecia assustador e não fazia nada.

Aí alguém me mostrou um app Android fazendo a mesma coisa para todas as partidas da Copa do Mundo — mas "do jeito certo", com configs criptografadas, Firebase Remote Config, um servidor de licenças, tudo. Uma seção dedicada "MUNDIAL FIFA 2026" com 28 canais, incluindo multicam, câmera do jogador e câmera do técnico para cada jogo. Levou cerca de uma hora para perceber que era a mesma vulnerabilidade vestindo um terno mais bonito.

O que começou como trinta minutos de curiosidade virou um fim de semana inteiro. No fim, eu tinha mapeado **670 transmissões ao vivo em 169 CDNs em 33 países** pelo lado da web, e **mais 525 canais de um único app Android** — todos protegidos por um esquema de DRM que a própria W3C chama de ferramenta de "teste". Durante uma Copa do Mundo em que a Inglaterra está aplicando quatro na Croácia e cada gol está simultaneamente disponível em centenas de transmissões não autorizadas.

{{< admonition type="warning" title="Aviso" open=true >}}
**Esta é uma análise de vulnerabilidade.** Nenhuma chave real, URL de transmissão ou ferramenta funcional é publicada. Nomes de apps e domínios estão omitidos. O objetivo é documentar uma fragilidade sistêmica na forma como as plataformas implementam DRM — não viabilizar a pirataria.
{{< /admonition >}}

## O Cadeado Com a Combinação Escrita no Verso

Todo navegador que reproduz vídeo criptografado usa as Encrypted Media Extensions (EME) da W3C. As EME suportam quatro sistemas de chave. Três deles — Widevine, FairPlay, PlayReady — usam servidores de licença, trocas de chave criptografadas e descriptografia respaldada por hardware. O quarto é o ClearKey.

O ClearKey envia a chave de descriptografia ao navegador em texto puro.

É isso. A mesma criptografia AES-128 do Widevine. A mesma entrega via MPEG-DASH. Mas onde o Widevine negocia chaves por um canal seguro e descriptografa o vídeo dentro de um sandbox de hardware no qual o navegador não consegue espiar, o ClearKey entrega a chave bruta ao JavaScript e diz "aqui está".

```
    WIDEVINE L1                          CLEARKEY
    ───────────────────────              ───────────────────────
    License server (HTTPS)               No license server
    Encrypted key exchange               Key in plaintext JS
    Hardware TEE decryption              Software decryption
    Key never in JS memory               Key IS the JS
    Per-device, per-session policy       No policy at all
```

O DASH Industry Forum afirma que o ClearKey é "recomendado apenas para fins de teste". Algumas plataformas decidiram usá-lo em produção mesmo assim. Tanto os sites quanto o app que analisei dependem dessa decisão.

## Parte 1: Os Sites

Examinei dezenas de sites de streaming pirata. Todos seguem o mesmo padrão.

### A Arquitetura

```
    ┌──────────────────────────────────────────────────────────────┐
    │                    HOW IT ACTUALLY WORKS                     │
    └──────────────────────────────────────────────────────────────┘

    STEP 1                    STEP 2                    STEP 3
    User visits               Clicks a channel          Page loads an <iframe>
    pirate-site.co            (e.g. "Sky Sports")       from a different domain

    ┌──────────────┐          ┌──────────────┐          ┌──────────────────┐
    │              │          │  ┌────┐┌────┐│          │  player-host.co  │
    │  Channel     │  click   │  │ESPN││DAZN││  iframe  │                  │
    │  Grid Page   │ ───────> │  ├────┤├────┤│ ───────> │  Shaka Player    │
    │              │          │  │Sky ││beIN││          │  + DRM keys      │
    │  (static     │          │  └────┘└────┘│          │  + manifest URL  │
    │   HTML)      │          └──────────────┘          └────────┬─────────┘
    └──────────────┘                                             │
                                                                 │ fetches
                                                                 ▼
                                                    ┌──────────────────────┐
                                                    │  LEGITIMATE CDN      │
                                                    │  (akamaized.net,     │
                                                    │   skycdp.com,        │
                                                    │   indazn.com)        │
                                                    │                      │
                                                    │  Encrypted DASH      │
                                                    │  segments            │
                                                    └──────────────────────┘

    The pirate site serves ZERO video.
    The CDN bill belongs to the legitimate provider.
    The pirate site hosts ~50KB of HTML.
```

O site pirata é apenas um intermediário que conhece duas strings hexadecimais. Ele aponta o navegador do espectador para um CDN real, entrega a chave de descriptografia, e o navegador faz o resto. O CDN nunca sabe que o espectador não é um assinante pagante.

### A Chave Está no Código-Fonte

A página do host do player contém um bloco `<script>` com as chaves, envolto em uma ofuscação que um script Node de 30 linhas desfaz em menos de um segundo:

```javascript
// Before: 40 lines of _0x4a2f, IIFEs, string-array lookups
// After: this is what it actually does

var drmKeyId = 'a1b2c3d4e5f6a7b80000000000000000';
var drmKey   = '0123456789abcdef0123456789abcdef';

player.configure({
    drm: { clearKeys: { [drmKeyId]: drmKey } }
});
player.load('https://cdn.legitimate-provider.com/manifest.mpd');
```

Duas variáveis. Duas strings hexadecimais. Essa é toda a "proteção DRM". A ofuscação — renomeação de variáveis, arrays de strings, achatamento de fluxo de controle — é desfeita pelo `eval()`, porque o navegador também tem que executá-la. Se o navegador consegue executar, um script consegue executar.

Uma vez extraídas, as chaves se espalham por playlists M3U no GitHub e no Telegram. Encontrei um único arquivo M3U com **545 entradas ClearKey em 92 CDNs**. Público, pesquisável, indexado pelo Google. Tempo da extração até a distribuição global: minutos. Tempo para a chave ser revogada: geralmente nunca.

## Parte 2: O App Android

Nem toda operação pirata é uma página web estática. Algumas distribuem um app Android completo — com telas de login, analytics do Firebase, configs criptografadas e uma UI profissional. Um app que analisei durante a Copa do Mundo segue essa abordagem. 525 canais. 36 categorias. Uma seção dedicada "MUNDIAL FIFA 2026" com ângulos multicam para cada partida.

{{< image src="images/app-config.png" caption="A config criptografada de canais do app — 525 canais em 36 categorias, com URLs criptografadas em AES e credenciais de DRM. [URLs omitidas]" >}}

### A Criptografia (AES-128-ECB)

O app busca sua lista de canais como um JSON criptografado em AES de um servidor remoto. Cada campo — URLs de transmissão, URIs de licença de DRM, cabeçalhos HTTP — é texto cifrado AES codificado em base64. A chave de descriptografia é uma string de 16 caracteres armazenada no Firebase Remote Config, buscada na inicialização.

Parece sério. Aí você olha o texto cifrado:

```
    236 URLs share the same first 35 encrypted blocks.

    Block 0: f91b7f273abccc6e...  ← identical across all 236
    Block 1: 932b23560f1d43ba...  ← identical
    Block 2: a7d788a399cea962...  ← identical
    ...

    ECB mode. Same plaintext block = same ciphertext block.
    The URLs all start with the same CDN domain prefix.
```

O AES-ECB é o exemplo clássico de como *não* usar AES. Todo curso de criptografia ensina isso com o [pinguim do ECB](https://blog.filippo.io/the-ecb-penguin/) — criptografe um bitmap com ECB e a imagem ainda é reconhecível porque blocos de texto puro idênticos produzem blocos de texto cifrado idênticos. Este app criptografa 236 URLs que começam com o mesmo domínio de CDN, produzindo 236 textos cifrados com um **prefixo idêntico de 560 bytes**. A análise de padrões por si só revela a estrutura antes mesmo de você encontrar a chave.

### O "Servidor de Licenças" Que Não É

Uma vez descriptografadas, as credenciais ClearKey não estão em uma troca de chaves separada. Elas estão na **própria URI de licença**, como parâmetros de query em texto puro:

```
    drm_license_uri (after decryption):

    https://[redacted]/?keyid=49eb924b...&key=6e131b04...
                              ^^^^^^^^        ^^^^^^^^
                              ClearKey ID     ClearKey Key
                              (in the URL)    (in the URL)
```

O "servidor de licenças" do app é uma URL que *contém a chave*. Não há desafio-resposta. Nenhuma vinculação de sessão. O app busca a URL, a URL *é* a chave, e a chave descriptografa a transmissão.

Três camadas de indireção — Firebase Remote Config, criptografia AES, um endpoint de "servidor de licenças" — todas colapsando na mesma falha: a chave de descriptografia acaba no cliente, em texto puro, porque o ClearKey exige isso.

### A Stack Completa do App

```
    ┌──────────────────────────────────────────────────────────────┐
    │           ANDROID APP ARCHITECTURE                           │
    └──────────────────────────────────────────────────────────────┘

    1. App launches
       └──> Firebase Remote Config
            └──> Fetches AES key ("claveapp") + config URLs

    2. App fetches encrypted JSON (525 channels)
       └──> Base64 decode ──> AES-128-ECB decrypt
            └──> Stream URLs, DRM license URIs, headers (plaintext)

    3. For CLEARKEY channels (348 of 525):
       └──> "License URI" = https://[redacted]/?keyid=XXX&key=YYY
            └──> Key ID and Key are IN THE URL

    4. App configures ExoPlayer with ClearKey
       └──> Fetches DASH manifest from legitimate CDN
            └──> Decrypts video with the key from step 3

    Package name: com.example.myapplication
    Encryption:   AES/ECB/PKCS5Padding (textbook insecure)
    Key storage:  Firebase Remote Config (single API call to extract)
    Key length:   16 characters, static, never rotated
```

O nome do pacote `com.example.myapplication` diz tudo sobre o rigor de desenvolvimento por trás dessa operação. O template padrão do Android Studio. Nem renomeado.

### A Lista de Canais ao Vivo Mora em um Repo Público

A parte mais silenciosamente condenatória — e a parte que mostra a *real* sofisticação da operação, que é majoritariamente logística — é onde a config criptografada está hospedada. O app busca seu JSON de canais em um repositório público do GitHub. Qualquer um pode navegá-lo. Qualquer um pode dar `git clone` nele. Qualquer um pode acompanhar o histórico de commits.

E o histórico de commits é a parte interessante:

{{< image src="images/commits.png" caption="O repositório público do GitHub que hospeda a lista de canais criptografada do app. Um commit aterrissa a cada 4–5 minutos — edições automatizadas no JSON de canais conforme as transmissões upstream são rotacionadas, derrubadas ou substituídas. Nome de usuário e dono do repo omitidos." >}}

Um commit aterrissa a cada 4–5 minutos. O diff é sempre contra `tv (10).json` ou `bearer.json` — a lista de canais criptografada, e as credenciais de autenticação usadas para buscar novas transmissões do upstream. As mensagens de commit são todas `"Actualizar tv (10).json desde tv.json"` ou `"Actualizar bearer.json desde panel - <timestamp>"`. Tradução: um job automatizado, em algum lugar, está reescrevendo a config de canais a cada poucos minutos e empurrando a atualização para que os celulares rodando o app peguem novas URLs de transmissão e credenciais de DRM rotacionadas dentro de um intervalo de polling.

Este é o verdadeiro fosso da operação. Não o AES-ECB (quebrado). Não o Firebase Remote Config (uma chamada de API). Não o "servidor de licenças" (uma URL com a chave dentro). O fosso é **operacional**: uma peça de automação que mantém o JSON de canais alinhado com quaisquer feeds upstream que estejam vivos em qualquer minuto dado, hospedada à plena vista em um CDN gratuito que eles não precisaram construir. Quando um provedor legítimo rotaciona uma chave, o bot percebe, busca a nova, recriptografa a config e faz o commit. Quando um CDN bloqueia o IP de origem, o bot encontra outro. Os celulares dos clientes fazem polling, veem o novo commit, buscam a nova config e continuam assistindo à partida. O usuário nem vê a rotação acontecer.

A defesa que todo mundo recorre é "revogar a chave". Mas você não consegue revogar uma chave mais rápido do que um script consegue empurrar uma nova para um repositório do GitHub. O descompasso na cadência é o jogo inteiro.

## Os Números

| | Auditoria web | App Android |
|---|---|---|
| **Transmissões** | 670 | 525 (348 ClearKey) |
| **CDNs** | 169 | 34 |
| **Países** | 33 | ~8 (foco na América Latina) |
| **Armazenamento da chave** | Bloco `<script>` JavaScript | Firebase Remote Config |
| **Ofuscação** | Renomeação de variáveis JS + IIFEs | AES-128-ECB (quebrado) |
| **Chave em texto puro no cliente** | Sim | Sim |
| **Cobertura da Copa do Mundo** | Dezenas de canais de esportes | 28 canais dedicados + multicam |

Ambas as abordagens — o site estático e o app Android completo — terminam no mesmo lugar: um par de chaves ClearKey na memória do cliente, sem mecanismo para impedir a extração, sem vinculação de sessão, sem revogação e sem rotação de chaves.

## O Dinheiro, e Por Que Não Vai Parar

A economia explica tudo. Sites piratas hospedam ~50KB de HTML estático e apontam seu navegador para o CDN de outra pessoa. O app é um wrapper fino de ExoPlayer em torno da infraestrutura de outra pessoa. Nenhum serve vídeo. Ambos monetizam através de redes de anúncios.

O StreamEast teve 1,6 bilhão de visitas antes de ser fechado em setembro de 2025. A rede de IPTV da "Operação Takendown" tinha 22 milhões de usuários e EUR 250 milhões por mês. Um site de médio porte durante a Copa do Mundo — um milhão de visitantes no dia do jogo, seis impressões de anúncio cada, CPM de US$ 1,50 — fatura **US$ 9.000 por dia** contra custos de hospedagem essencialmente nulos.

A fiscalização não para de escalar — penas de prisão na Espanha, sentenças de US$ 18,75 milhões no Texas, 27.000 feeds derrubados na semana anterior à Copa do Mundo — e novos sites continuam aparecendo. Porque a vulnerabilidade é arquitetural. Você não consegue prender o caminho para fora de uma especificação que coloca a chave de descriptografia no navegador. Você também não consegue criptografar o caminho para fora dela, como o app Android demonstra: três camadas de criptografia, e a chave ainda acaba em texto puro no cliente, porque o ClearKey exige isso.

A correção é migrar inteiramente para fora do ClearKey — para Widevine, FairPlay ou PlayReady. Até lá, a combinação está escrita no verso do cadeado, e a Copa do Mundo está garantindo que todo mundo saiba onde olhar.

