Como eu hackeei o Steam. Duas vezes

Olá Habr! Hoje, vou explicar por que a Valve pagou as maiores recompensas da história do programa de recompensas por vulnerabilidades. Bem-vindo ao gato!



1. Injeção de SQL


O serviço partner.steampowered.com foi projetado para receber informações financeiras dos parceiros Steam. Na página do relatório de vendas, um gráfico é desenhado com botões que alteram o período em que as estatísticas são exibidas. Aqui estão eles no retângulo verde:



A solicitação de download de estatísticas é semelhante a esta:


onde "UA" é o código do país.

Bem, então, é hora de citações!
Vamos tentar "UA":



As estatísticas NÃO retornaram, o que é esperado.

Agora "UA ''":



As estatísticas estão de volta e parece uma injeção!

Porque
Digamos que a instrução do banco de dados tenha esta aparência:

SELECT * FROM countries WHERE country_code = `UA`; 

Se você enviar UA ', a instrução do banco de dados será:

 SELECT * FROM countries WHERE country_code = `UA``; 

Notou uma cotação extra? E isso significa que a instrução é inválida.
De acordo com a sintaxe SQL, a consulta abaixo é completamente válida (não há aspas extras):

 SELECT * FROM countries WHERE country_code = `UA```; 

Observe que estamos lidando com uma matriz de countryFilter [] . Supus que, se duplicarmos o parâmetro countryFilter [] na consulta várias vezes, todos os valores que enviarmos serão combinados na consulta SQL desta maneira:

 'value1', 'value2', 'value3' 

Verificamos e garantimos:



De fato, solicitamos estatísticas de três países no banco de dados:

 `UA`, `,` ,`RU` 

A sintaxe está correta - as estatísticas estão de volta :)

Ignorar o firewall do aplicativo Web

Servidores a vapor estão escondidos atrás do Akamai WAF. Essa desgraça insere varas nas rodas de bons (e não tão) hackers. No entanto, consegui superá-lo combinando os valores da matriz em uma consulta (o que expliquei acima) e comentando. Primeiro, verifique se o último está disponível:

 ?countryFilter[]=UA`/*&countryFilter[]=*/,`RU 

A solicitação é válida, portanto, há comentários em nosso sortimento.
Tínhamos várias opções de sintaxe, bancos de dados locais para testar cargas, caracteres de comentário e um número infinito de cotações para todas as codificações, além de scripts auto-escritos em python, documentação para todos os bancos de dados, instruções sobre como ignorar firewalls, wikipedia e antichest. Não é uma reserva necessária para a promoção de injeção, mas desde que começou a quebrar o banco de dados, é difícil parar ...
O WAF bloqueia uma solicitação quando encontra uma função nela. Você sabia que DB_NAME / ** / () é uma chamada de função válida? O firewall também conhece e bloqueia. Mas, graças a esse recurso, podemos dividir a chamada de função em dois parâmetros!

 ?countryFilter[]=UA',DB_NAME/*&countryFilter[]=*/(),'RU 

Enviamos uma solicitação com DB_NAME / * de qualquer maneira * / () - o WAF não entendeu nada, mas o banco de dados processou com êxito essa instrução.

Recuperando valores de um banco de dados

Portanto, um exemplo de como obter o comprimento de um valor DB_NAME ():

 https://partner.steampowered.com/report_xml.php?query=QuerySteamHistory&countryFilter[]=',(SELECT/*&countryFilter[]=*/CASE/**/WHEN/*&countryFilter[]=*/(len(DB_NAME/*&countryFilter[]=*/())/*&countryFilter[]=*/=1)/**/THEN/**/'UA'/**/ELSE/*&countryFilter[]=*/'qwerty'/**/END),' 

No SQL:

 SELECT CASE WHEN (len(DB_NAME())= 1) THEN 'UA' ELSE 'qwerty' END 

Bem, humanamente:

   DB_NAME()  "1",   “UA”,   “qwerty”. 

Isso significa que, se a comparação for verdadeira, em troca, obteremos estatísticas para o país "UA". Não é difícil adivinhar que, passando de valores de 1 ao infinito, mais cedo ou mais tarde encontraremos o correto.

Da mesma maneira, você pode iterar sobre os valores de texto:

    DB_NAME()  “a”,  "UA",  "qwerty". 

Normalmente, a função "substring" é usada para obter o enésimo caractere, mas o WAF o bloqueia teimosamente. Aqui a combinação veio ao resgate:

 right(left(system_user,N),1) 

Como isso funciona? Nós obtemos N caracteres do valor de system_user do qual extraímos o último.
Imagine esse system_user = "steam". É assim que a aparência do terceiro personagem será:

 left(system_user,3) = ste right(“ste”,1) = e 

Com um script simples, esse processo foi automatizado e obtive o nome do host, o usuário do sistema, a versão e os nomes de todos os bancos de dados. Essa informação é mais do que suficiente (a última é até supérflua, mas era interessante) para demonstrar criticidade.

Após 5 horas, a vulnerabilidade foi corrigida, mas o status de triagem foi definido após 8 horas e, caramba, foram 3 horas muito difíceis para as quais meu cérebro conseguiu sobreviver aos estágios da negação à aceitação.

Explicação da paranóia
Como a vulnerabilidade não foi designada como aceita, eu acreditava que a linha ainda não havia atingido meu relatório. Mas eles corrigiram o erro, o que significa que eles poderiam tê-lo registrado antes de mim.

2. Obtendo todas as chaves para qualquer jogo


Na interface do parceiro Steam, há uma funcionalidade para gerar chaves de jogo.
Você pode fazer o download do conjunto de chaves gerado usando a solicitação:

 https://partner.steamgames.com/partnercdkeys/assignkeys/ &sessionid=xxxxxxxxxxxxx&keyid=123456&sourceAccount=xxxxxxxxx&appid=xxxxxx&keycount=1&generateButton=Download 

Nesta solicitação, o parâmetro keyid é o ID do conjunto de chaves e keycount é o número de chaves que devem ser obtidas desse conjunto.

Obviamente, minhas mãos estenderam a mão instantaneamente para dirigir com diferentes códigos , mas um erro me aguardava: “Não foi possível gerar chaves de CD: nenhuma atribuição para o usuário. " Acabou que nem tudo era tão simples, e o Steam verificou se eu possuía o conjunto de chaves solicitado. Como consegui contornar esse teste? Atenção ...

 keycount=0 

Um arquivo foi gerado com 36.000 chaves para o jogo Portal 2. Uau.
Somente em um conjunto havia esse número de chaves. E todos os conjuntos no momento mais de 430.000. Assim, classificando os valores de keyid, eu era um invasor em potencial que podia baixar todas as chaves já geradas pelos desenvolvedores de jogos Steam.

Conclusões


  • Os dispendiosos sistemas WAF das principais empresas estão longe de garantir a segurança de seus aplicativos da web.
  • Se você é um caçador de insetos, tente penetrar o mais fundo possível. Quanto menos usuários tiverem acesso a uma interface, maior a probabilidade de encontrar uma vulnerabilidade nessa interface.
  • Desenvolvedores e proprietários de empresas, não há aplicativos absolutamente seguros! Mas você aguenta. Tenha um bom humor!

Mas seriamente
Faça pentests, pague por vulnerabilidades, pense estrategicamente.

Source: https://habr.com/ru/post/pt421215/


All Articles