2 pontos por GN⁺ 2024-01-15 | 1 comentários | Compartilhar no WhatsApp
  • O ambiente de addons em Lua de World of Warcraft facilitava a alteração da UI, mas separava o acesso às informações das permissões de execução para impedir a automação de ações no jogo
  • O modelo de segurança da Blizzard dividia o sistema entre um ambiente não seguro, que lia muitas informações, e um ambiente seguro, que executava ações; essa fronteira era o núcleo da prevenção contra automação
  • O random permitido no ambiente seguro era um wrapper fino em torno do rand da biblioteca C do MSVC e, por ser um gerador congruencial linear fraco com estado global compartilhado, podia se tornar um canal de transmissão de informações
  • Com apenas duas saídas do RNG, era possível reduzir os candidatos ao estado interno para no máximo 8; com uma saída adicional, escolher um único candidato e então avançar o RNG até obter o índice da ação desejada
  • Por fora parecia uma escolha aleatória de ação, mas na prática era possível fazê-lo selecionar uma ação desejada, como uma magia; não está claro de que forma a Blizzard corrigiu isso depois

Addons de World of Warcraft se tornaram uma porta de entrada para a programação

  • Os addons de World of Warcraft tinham uma estrutura em que bastava colocar alguns arquivos-fonte .lua, escritos em Lua, dentro de uma pasta para que o jogo os carregasse diretamente
  • Depois de editar e salvar um arquivo, recarregar a interface mostrava o resultado imediatamente, então a barreira de entrada era muito baixa
  • No começo, vários addons foram criados copiando o código de outras pessoas e refatorando ou recombinando trechos para montar as funcionalidades desejadas
  • Com o tempo, a criação de addons passou a consumir mais tempo do que o próprio gameplay, e World of Warcraft acabou sendo o gatilho para começar a programar de forma mais séria

O modelo de segurança de addons da Blizzard

  • Se usuários pudessem criar addons totalmente programáveis, isso poderia gerar problemas com bots; por isso, a Blizzard criou uma estrutura que limitava a automação possível para jogadores comuns
  • A maioria dos elementos de UI e das APIs de coleta de informações podia ser usada sem restrições
    • Por exemplo, era possível ler a vida do personagem e criar uma barra de vida que ajustava o tamanho de um frame em primeiro plano
  • APIs que executavam ações reais no jogo, como mover o personagem, lançar magias ou usar itens, eram protegidas e só podiam ser chamadas pelo código oficial da Blizzard
  • APIs para obter a posição exata no mundo e a direção da câmera também passaram a ser protegidas em algum momento
    • Foi uma medida contra addons que desenhavam elementos 3D sobre o mundo do jogo para facilitar lutas contra chefes

Separação entre ambiente seguro e não seguro

  • Alguns elementos de UI precisavam interagir com ações reais do jogo, então era possível criar botões especiais que executavam código no ambiente seguro quando clicados
  • Esses botões não podiam ser criados, apagados nem movidos durante o combate
    • Isso impedia a automação por meio da colocação condicional de botões sob o cursor durante o combate
  • O ambiente seguro podia definir programaticamente qual magia lançar, mas bloqueava o acesso a estados externos necessários para automação arbitrária
  • Apenas algumas APIs de coleta de informações no nível do sistema de macros eram permitidas; informações necessárias para escolher a melhor magia, como cooldowns de habilidades ou vida de unidades, não eram fornecidas
  • Como resultado, os dois ambientes tinham permissões diferentes
    • Ambiente não seguro: consegue obter muitas informações, mas não consegue agir
    • Ambiente seguro: consegue agir, mas não consegue obter as informações necessárias para automação

random virou um canal de backdoor

  • O objetivo não era desativar as restrições de segurança com software de terceiros, e sim verificar se era possível contornar o ambiente seguro usando apenas ferramentas tecnicamente permitidas
  • Ao examinar a lista de funções permitidas no ambiente seguro, foi encontrado o random
  • Como a maioria dos geradores de números aleatórios em computadores são geradores pseudoaleatórios com estado interno, concluiu-se que, se fosse possível manipular esse estado, seria possível transmitir informações externas para o ambiente seguro
  • Na prática, random era um pequeno wrapper em torno do rand da C
  • Como World of Warcraft era compilado com MSVC, a implementação de rand era um gerador baseado em estado global
uint32_t state;

int rand() {
    state = state * 214013 + 2531011;
    return (state >> 16) & 0x7fff;
}
  • Essa implementação compartilhava um único estado global de números aleatórios no processo e era um gerador congruencial linear fraco

Recuperação do estado do rand do MSVC

  • A saída de rand expõe diretamente 15 bits, que são parte do estado
  • Como o espaço de estados é pequeno e parte da saída é visível, é possível manter candidatos de estado e eliminar candidatos com saídas adicionais
  • Uma forma mais eficiente é observar duas saídas do RNG, r0 e r1, e calcular os candidatos para a parte oculta de 16 bits do estado
  • As constantes do rand do MSVC são as seguintes
    • a = 214013
    • b = 2531011
  • Chamando de h0 e h1 as partes ocultas de 16 bits após a primeira e a segunda chamadas, respectivamente, é possível derivar candidatos para h0 a partir da equação de transição de estado
  • O resultado do cálculo mostra que basta considerar k no intervalo -1 <= k < 7, reduzindo os candidatos ao estado oculto para no máximo 8
  • Em seguida, compara-se o resultado de chamadas adicionais ao RNG com a saída dos RNGs candidatos para remover os candidatos incorretos
  • O exemplo de implementação em Python usava um loop while, mas, na prática, parecia que até a terceira saída já era suficiente para reduzir a um único candidato
  • Na época, como era difícil fazer essa derivação de matemática discreta diretamente, foi feita uma pergunta no crypto.SE, com a justificativa de que “queria mostrar a um colega o quanto esse RNG era fraco”

Como escolher a ação desejada no ambiente seguro

  • Depois que era possível recuperar o estado interno do RNG, tornava-se possível transmitir uma decisão de automação arbitrária ao ambiente seguro
  • O fluxo era o seguinte
    • Registrar um hook não seguro executado imediatamente antes do código do ambiente seguro
    • Dentro do hook, como havia acesso a todas as informações, decidir qual ação executar
    • Procurar a ação decidida em uma lista de ações hardcoded e convertê-la em um índice
    • Recuperar o estado atual do RNG usando o método anterior
    • Se a próxima saída do RNG, considerada pelo resto da divisão pelo tamanho da lista de ações, não fosse o índice desejado, avançar o RNG e verificar novamente
    • Quando chegasse a vez da saída desejada, o hook retornava, e o ambiente seguro chamava random para executar a ação daquele índice
  • No ambiente seguro, parecia que a lista de ações estava sendo indexada por um número aleatório, mas, na prática, o momento da próxima chamada a random era controlado para que a ação desejada fosse escolhida
  • Se o tamanho da lista de ações fosse n, em média era preciso avançar o RNG n passos para transmitir o valor desejado, mas isso não era um problema no uso real

Situação posterior

  • Alguns anos depois, ao tentar novamente, esse método já não funcionava
  • Não se sabe quando a Blizzard corrigiu o problema do estado de RNG fraco e compartilhado, nem se ela o reconheceu como um problema
  • Possíveis mudanças incluem a troca para outro algoritmo ou a adoção de um estado de RNG devidamente separado para o ambiente seguro
  • Embora não tenha sido um código destinado a uso prático, foi um caso restrito de bypass de segurança em jogo que conseguiu manipular algo aparentemente aleatório para obter o resultado desejado

1 comentários

 
GN⁺ 2024-01-15
Opiniões no Hacker News
  • Há muito tempo, quando eu trabalhava como GM de WoW, uma versão específica do popular addon Titan Bar tinha um bug que chamava continuamente a API que abria a interface de “criar ticket para um GM”
    Mesmo se você cancelasse, ela aparecia de novo; e, fora remover o addon problemático, só dava para se livrar da janela escrevendo alguma coisa na caixa de texto e enviando
    Do ponto de vista de um GM, foram um ou dois dias realmente difíceis, e muitos jogadores nem sabiam o que era um GM, nem que era possível enviar tickets
    Também não costumava ser fácil explicar que “você provavelmente enviou um ticket por causa de um addon, então eu, do suporte ao cliente, estou entrando em contato”
    Como isso basicamente destruiu os tempos normais de resposta, os desenvolvedores lançaram um hotfix e colocaram essa chamada de API atrás de uma proteção, permitindo-a apenas a partir do código LUA da própria Blizzard

    • Acho que o nome era Titan Panel
      Era um addon que reunia vários elementos de UI, como total de ouro e porcentagem de reparo, em uma barra única
  • Curiosamente, eu havia respondido no StackOverflow, alguns meses antes de a pergunta aparecer no Crypto.SE, a uma questão quase igual sobre “resolver um gerador de números aleatórios ao contrário”: https://stackoverflow.com/a/15237585/1204143
    Só que ataquei o gerador de números aleatórios do Java em vez do gerador da MSVC
    Ambos são geradores congruenciais lineares (LCG) simples que emitem valores truncados do estado interno, então os ataques também são muito parecidos

    • Como não sou muito bom em matemática, pedi ao Z3 que encontrasse sementes convenientes, e o resultado foi o código abaixo
      import java.util.Random;
      public class Hayley {
      public static void main(String... args) {
      byte[] b1 = new byte[4], b2 = new byte[2];
      (new Random(0x2effe2140e00L)).nextBytes(b1);
      (new Random(0xc2f0097)).nextBytes(b2);
      System.out.println(new String(b1) + new String(b2));
      }
      }
  • Fico me perguntando se havia a possibilidade de o servidor usar o mesmo gerador de números aleatórios
    Nesse caso, com um servidor de uso muito baixo, baixa latência, capacidade de restringir com bastante precisão o estado atual do gerador de números aleatórios e capacidade de fazer ocorrer rapidamente de novo um evento com grande variação econômica dependendo do número aleatório, parece teoricamente explorável

    • Os servidores de Vanilla WoW usavam o Mersenne Twister e, embora ele tivesse vários problemas, não era quebrável de forma tão trivial
      A Blizzard divulgou muitos detalhes sobre como a geração de números aleatórios no lado do servidor funcionava, em uma tentativa em grande parte malsucedida de acabar com rumores de jogadores supersticiosos que acreditavam firmemente em coisas como uma “semente de loot”
  • A economia e os incentivos dos geradores de números aleatórios são interessantes
    Se não há nada de fato em jogo e as pessoas não conseguem perceber facilmente, geradores de números aleatórios imperfeitos, ou até ruins, podem ser suficientes em jogos
    Mas, quando dinheiro ou segurança estão envolvidos, como em carteiras de criptomoedas ou cassinos online, isso de repente se torna importantíssimo

  • Mais grave ainda: o gerador congruencial linear de números aleatórios apresentado só produz 12.445 números diferentes antes de se repetir
    Só usar parâmetros melhores nesse algoritmo já teria sido muito mais razoável

    • Engenharia é compromisso
      Para tarefas simples, como o movimento de partículas em rastros de fumaça, é preciso um gerador de números aleatórios rápido e simples
      Em criptografia, jogos de cassino etc., além de usar um gerador de números aleatórios melhor, com pelo menos 32 bytes de estado, às vezes o motor também é avançado em intervalos aleatórios para dificultar ainda mais a previsão dos resultados
  • World of Warcraft foi o que me fez me interessar por programação
    Eu tinha 6 anos quando foi lançado e, quando servidores privados começaram a aparecer, fiquei curioso sobre como criar meus próprios NPCs e aprendi Lua
    Muitos posts do fórum MMOwned em que eu participava naquela época ainda existem, e uma das pessoas que me ajudaram a aprender ainda posta de vez em quando
    Ao compilar eu mesmo o core de servidores privados, aprendi sobre compilação, SVN e aplicação de patches; acho que havia algum bug relacionado ao Fireball do mago, mas não me lembro exatamente

    • Sinto nostalgia sempre que lembro dos fóruns antigos
      Hoje parece que boa parte do que havia de bom na internet foi substituída por apps de redes sociais que induzem dopamina e por fóruns fechados como o Reddit
      Provavelmente ainda fico no HN porque ele me lembra os fóruns de antigamente
  • “Qualquer pessoa que considere métodos aritméticos para produzir dígitos aleatórios está, é claro, em estado de pecado. Como já foi apontado várias vezes, não existe número aleatório. Existem apenas métodos para produzir números aleatórios, e um procedimento aritmético estrito, obviamente, não é esse método.” — John von Neumann

    • Como criptógrafo, acho essa citação realmente difícil de suportar
      Não tenho nada contra Neumann, mas as pessoas que a espalham como se fosse um aforismo sábio geralmente só transmitem desinformação
      No contexto, ela está mais para uma piada filosófica desnecessária
      Já temos há décadas geradores criptográficos de números aleatórios rápidos e confiáveis
      Eles funcionam corretamente só com uma semente muito pequena e operações aritméticas; os números gerados são indistinguíveis de qualquer outra fonte aleatória correta e podem ser usados para qualquer finalidade
      Muita gente não entende isso, e até surgem empresas vendendo a panaceia de “números aleatórios de verdade”
  • Um bom exemplo de gerador de números aleatórios realmente horrível está em Monster Hunter 3 Ultimate, e talvez também em outros títulos
    Além de ser totalmente explorável para obter bons loots, ele também criou as “charm tables”
    Alguns itens têm atributos gerados aleatoriamente, mas a semente inicial é salva no arquivo de save, limitando a faixa de itens que você pode obter
    Se você desse azar, podia cair em uma “tabela amaldiçoada”, que era basicamente um gerador de números aleatórios com período muito curto, e nunca conseguir os valores máximos
    Não chegava a arruinar o jogo, mas era ruim para o jogo em alto nível

  • Eu escrevi scripts para a casa de leilões
    Eles escaneavam todos os leilões, compravam itens subprecificados e os listavam de novo
    Se alguém listasse mais barato, eu podia cancelar e listar de novo um pouco abaixo do preço dessa pessoa; na prática, isso gerava ouro ilimitado

    • Depois que pessoas bêbadas de verdade que trabalhavam no Goldman me explicaram como o dinheiro circula, expliquei isso ao meu irmão, e ele respondeu: “então o IRS é um sumidouro de ouro?”
      “Hã, na verdade é uma analogia bem boa. A diferença é… hã… é uma analogia bem boa!”
    • Isso é basicamente igual ao trading de alta frequência no mundo real
    • Por acaso você criou o Auctioneer? Ou era algo parecido com o Auctioneer, mas você decidiu não usar a ferramenta “padrão do setor”?
    • Fiquei curioso: como você determinava se algo estava subprecificado?
    • Você é o criador do addon Auctioneer? Se sim, ele deve ter sido um dos addons de WoW mais populares por mais de 10 anos até agora
  • “Quando variáveis não variam e constantes não são constantes”

    • Interessante. Parece um meme de raízes profundas
      https://www.theregister.com/2006/07/26/constants_are_not/
      “Li pela primeira vez em Creative Computing, alguns anos antes de C ser criado, o lamento sobre os caprichos da programação que está no título deste artigo: ‘Variables Won't Constants Aren't’.”
      Se houver algum texto específico que você considere a versão canônica desse meme, eu gostaria de ler