胖虎的入住
合租屋原来隔壁的文静小伙搬走了,换来了一个胖胖的男生。也许是胖子天生对胖子的认同,我对胖子的印象都是非常不拘小节,且随和、好说话的那种。
而这位呢?确实不拘小节,但是实在太任性了。每天打游戏也就罢了,边打还边语音聊天,大叫大嚷个不停。白天如此,晚上12点过后还是如此。
同住的室友好言相劝过,也演变成指责谩骂过,但他的反应都是不言不语,依旧我行我素。依然不顾他人感受,还经常敞着房门,那叫喊声简直响彻屋顶。
最讨厌的是,打CF每每打到关键时刻,被爆头了还爱甩锅给队友!甩锅的同时还吐各种脏话侮辱队友,他队友还能忍他这么久,这我就不能忍了!
本着先礼后兵、先君子后小人的原则,既然劝阻无效,我决定采取一个措施。
限速
因为路由器的管理员密码是默认的“123456”,所以很容易就登录进管理页面。每当晚上要睡觉的时候,给他个限速。游戏打的不爽?早点儿睡吧。
顺便把路由器密码也改了,这样防止他再给自己解限速。
然而胖虎毕竟打了这么多年游戏了,并不傻,很快就发现被限速了。
于是开始敲门问其他住客,打电话问房东,到底密码是啥?!但并没有人知道……
某一天,胖虎终于忍不了了,重置了路由器,恢复了默认密码,SSID改成和原来的一样。
简直机智。
新的对策
又恢复了夜晚的嘶吼,实在是令人沮丧。
这回我和女票一起开始商量新一轮的制裁了。我们有以下三个前提:
- 直接限速是不可能的了,这边改密码,那边就重置,根本不是办法。
- 在不影响其他人正常使用的情况下,给他限速或断网。
- 不能被他发现是人为因素导致的。
ARP欺骗
女票说:“既然在同一个网关下,又能ping通,就可以ARP欺骗呀。
伪造ARP包,自己在路由器这边伪装被攻击的主机,在主机那头伪装成路由器,两头欺骗。”
但我们很快发现这也不是可行的方案,因为ARP欺骗是很容易检测到的,如果他电脑装了360的话,360是会有弹框报警的。
给路由器刷入openWRT
给路由器换个系统,相当于把路由器完全掌控在手中。不仅想限速就限速,还可以对外显示虚假的管理页面。
理论上可行,然而我们都没有这方面经验,而且要动手实现工作量有点忒大,属于杀鸡用牛刀了。
建一个同SSID名的热点蜜罐
设备碰到以前连过的wifi会自动连接,靠的是wifi的SSID(就是wifi的名称啦)。只要你开一个同名的wifi热点,他的设备就会选择信号最强的一个连接。
这时你只需要偷天换日,把路由器的wifi断一会儿,他就连上你的“蜜罐”啦,这时他的网速命运就掌握在你的手里了。
可是这个办法也行不通……因为胖虎这家伙,根本不用wifi。为了保证打游戏稳定,他是直接拉了根网线到自己屋子里的!
用脚本发送禁网请求,实现针对胖虎的间歇性禁网
最后这个方案是可行且成本最小的。
简单说就是,既然我们可以登录路由器管理页,就可以写个脚本直接发请求,实现管理页的所有管理操作。但如果永久禁网或限速的话,无疑像以前一样会被发现。所以既要给他断网,又不要太长时间,这样间歇性地断网。
开工
这个路由器是 TP-link 的,型号是 TL-WR842N。
1. 找到禁网的请求
首先进入管理页面:
打开 chrome,点开 network 可以看到这个页面每隔0.1秒就发送一个请求去获取每个设备当前的网速。
然后点击一下禁用按钮,会发送一个更上图类似的 POST 请求:
对比这个请求和上一个请求,有以下几个要点:
- url参数 code=0
- url参数 id 好像是个 token 值,隔一段时间就会变化一次。
- POST 的内容是设备的 mac地址+设备名称+上传限速+下载限速+是否禁网
这其中(1)和(3)都不是问题,如果我要禁某个设备的网,这两部分都是固定的。
不好搞的是这个 id 值,每几秒就会变化一次,而这个 id 值是怎么得到的呢?在所有请求当中都完全找不到影子,在 HTML当中也找不到。看来它是通过某种算法算出来的。
抓包到这一步没有什么好办法了,只能靠看前端 js 源码了。
2. 找到点击“禁网”按钮的事件代码
照我以前的尿性,就是拿起js代码就开始看了,代码很多的时候要摸索很久。
这时女票教了我一个新姿势,啊不,是新知识。查看页面元素的事件:
简单说就是找到目标元素,在 Elements
里选择 Event Listeners
然后点开相应的事件,就能找到绑定的函数了~ 如下图
这个过程中还有点儿小插曲:
前面说过了,这页面每隔0.1秒就会发送一个获取所有设备当前网速的请求,所以正中间的那个列表的 DOM 元素在不停地变化。当你点击“禁网按钮”所在元素的时候,因为 DOM 每0.1秒就变化一次,所以你根本就选不中目标元素。
这怎么办呢?很简单,只要在 Sources 当中点击右边的暂停按钮,当前正在执行的 js 就会全部暂停。这样 DOM 元素就不会变化了,你就能顺利选中你想选中的元素啦~
3. 找到token生成算法
找到元素绑定的函数后,顺藤摸瓜,找到了生成token的相关函数,直接贴代码:
1 |
|
剥茧抽丝,所有问题都转化为 this.session
是如何生成的。
代码审计到这一步,有三条路线可以走:
- 继续看 securityEncode 函数是怎么实现的,以及传入它的三个参数的来源。
- 不看 securityEncode 内部的复杂代码,用 Python 写发请求的部分,到了需要用到token的时候,调用 PyV8的JS引擎,直接把securityEncode函数丢进去,直接算出结果。
- 根本不用审计任何代码,使用 PantomJs、CasperJs、chrome插件等方式,直接在浏览器的环境里模拟点击按钮。
这三条路都行得通,有点像 脱离浏览器环境->半浏览器环境->全浏览器环境 的感觉。
女票的意思是直接用(3),但我还是喜欢刨根问底,于是女票就把写脚本的这个任务就交给我了……
4. 找到参数来源,移植加密算法
- authInfo[3]
- pwd
- authInfo[4]
上面是 securityEncode 的三个入参,要找到它们的来源。
(2)很好找,是个固定的字符串,就是管理员密码“123456”的固定映射。
找了半天(1)和(2),终于找到了……只能说写这个路由器管理页面的程序员想法挺有意思的——
前文反复说到,这个页面会每隔0.1秒发送一次获取当前网速的请求。这些请求大多数情况都是成功的,返回码都是200
。但是过几十秒钟的某次就会返回401 Unauthorized
这个401的请求会跟200返回的结果不一样:
200返回的结果就是每个设备的 IP、MAC、网速等等信息:
而401返回的结果是这样的:
这样就清晰了,authInfo[3]
和authInfo[4]
就是这个响应结果的第三行和第四行。
接下来就是把 securityEncode 函数的代码翻译成 Python 了。
还好,securityEncode函数的内容不长,算法也不复杂,而且没有混淆代码。否则的话只能用 PyV8了。
直接贴代码:
JS
1 | this.securityEncode = function(input1, input2, input3) |
Python
1 | def securityEncode(self, input1, input2, input3): |
5. 写脚本,开始“制裁”
很快Python脚本写好了,每隔 N 秒钟,就给他断一次网,每次断 M 秒。谁会受得了游戏不停地掉线呢?
禁网的 M 秒期间,是登不上路由器管理页面的。
当他登上去的时候,又解禁了,所以看不到任何的痕迹。
为了做到万无一失,禁网的几秒钟里会把他的手机和电脑一起禁掉,这样他就没有任何设备可以登陆管理页看到这是为什么了。
疗效
每天晚上要睡觉的时候,觉得太吵了,就把脚本一开……
一开始,断网的时候就听见胖虎在那儿叫:
“艹!掉线了!”
“卧槽!!又掉线了!”
“啊啊?怎么又 TM 掉线了?!”
“……”
到后来就再也听不到他的怒吼了……
如今,到了晚上12点,胖虎打游戏基本不会发出太大的声音,如果听见声音太大的话,我就直接开脚本。如果还不起作用,就把掉线时间间隔调小一点,断网时长调长一点。
胖虎依旧天天打游戏,但渐渐开始早睡了,相信他这个“掉线小王子”再也没有理由甩锅给队友了。
感觉又为这个世界的美好安详做出了小小的贡献,真好……