如果你总认为,你在访问某网站时,上传下载的数据没有任何价值,那为何仍有如此多滴黑客怪蜀黍,醉心于SSL劫持? 从黑客的思维思考隐私安全,你才知道该保护些什么!——搬运工语录 小编吭哧搬运过来,各位慢慢享用! 前言 在之前介绍的流量劫持文章里,曾提到一种『HTTPS 向下降级』的方案 —— 将页面中的 HTTPS 超链接全都替换成 HTTP 版本,让用户始终以明文的形式进行通信。 看到这,也许大家都会想到一个经典的中间人攻击工具 —— SSLStrip,通过它确实能实现这个效果。不过今天讲解的,则是完全不同的思路,一种更有效、更先进的解决方案 —— HTTPS 前端劫持。 后端的缺陷 在过去,流量劫持基本通过后端来实现,SSLStrip 就是个典型的例子。 类似其他中间人工具,纯后端的实现只能操控最原始的流量数据,这严重阻碍了向更高层次的发展,面临众多难以解决的问题。 动态元素怎么办? 如何处理数据包分片? 性能消耗能否降低? …… 动态元素 在 Web 刚出现的年代里,SSLStrip 这样的工具还是大有用武之地的。那时的网页都以静态为主,结构简单层次清晰。在流量上进行替换,完全能够胜任。 然而,如今的网页日益复杂,脚本所占比重越来越多。如果仅仅从流量上着手,显然力不从心。 var protocol = ‘https’;document.write(‘<a href=”‘ + protocol + ‘://www.alipay.com/”>Login</a>’); 即使非常简单的动态元素,后端也毫无招架之力。 分片处理 分块传输的道理大家都明白。对于较大的数据,一口气是无法传完的。客户端依次收到各个数据块,最终才能合并成一个完整的网页。 由于每次收到的都是残缺的碎片,这给链接替换带来很大的麻烦。加上不少页面并非标准的 UTF-8 编码,因此更是难上加难。 为了能顺利进行,中间人通常先收集数据,等到页面接收完整,才开始替换。 如果把数据比作水流,这个代理就像大坝一样,拦截了源源不断往下流的水,直到蓄满了才开始释放。因此,下游的人们需忍受很久的干旱,才能等到水源。 性能消耗 由于 HTML 兼容众多历史遗留规范,因此替换工作并非是件轻松事。 各种复杂的正则表达式,消耗着不少的 CPU 资源。尽管用户最终点击的只是其中一两个链接,但中间人并不知道将会是哪个,因此仍需分析整个页面。这不得不说是个悲哀。 前端的优势 如果我们的中间人能打入到页面的前端,那么情况会不会有所改善呢? 分片处理 首先,要派一名间谍到页面里。这是非常容易办到的: 不像超链接遍布在页面各处,脚本插入到头部即可运行了。所以我们根本不用整个页面的数据,只需改造下第一个 chunk 就可以,后续的数据仍然交给系统转发。 因此,整个代理的时间几乎不变! 动态元素 很好,我们轻易渗透到页面里。但接着又如何发起进攻? 既然到了前端里,方法就相当多了。最简单的,就是遍历超链接元素,将 https 的都替换成 http 版本。 这个想法确实不错,但仍停留在 SSLStrip 思维模式上。还是『替换』这条路,只是从后端搬到前端而已。 尽管这个方法能胜任大多场合,但仍然不是最完美的。我们并不知道动态元素何时会添加进来,因此需要开启定时器不断的扫描。这显然是个很挫的办法。 性能优化 事实上,超链接无论是谁产生的、何时添加进来的,只要不点击,都是不起作用的。所以,我们只需关心何时去点击就可以 —— 如果我们的程序,能在点击产生的第一时间里控制住现场,那么之后的流程就可由我们决定了。 听起来似乎很玄乎,不过在前端,这只是小菜一碟的事。点击,不过个事件而已。既然是事件,我们用最基础的事件捕获机制,即可将其轻松拿下: document.addEventListener(‘click’, function(e) { // …}, true); DOM-3-Event 是个非常有意义的事件模型。之前用它来实现『内联 XSS 拦截』,如今同样也可以用来劫持链接。 我们捕获全局的点击事件,如果发现有落在 https 超链接上,果断将其……拦截? 如果真把它拦截了,那新页面就不会出现了。当然你会说,可以自己 window.open 弹一个,反正点击事件里是可以弹窗的。 不过,请别忘了,并非所有的超链接都是弹窗,也有不少是直接跳转的。你也会说可以修改 location 来实现。 但要识别是『弹窗』还是『跳转』,并不简单。除了超链接的 target 属性,页面里的 <base> 元素也会有影响。当然,这些相信你都能处理好。 然而,现实未必都是那么简单的。有些超链接本身就绑定了 onclick 事件,甚至在其中 return false 或 preventDefault,屏蔽了默认行为。如果我们不顾及这些,仍然模拟跳转或弹窗,那就违背页面的意愿了。 事实上,有一个非常简单的办法:当我们的捕获程序运行时,新页面还远没出现,这时仍有机会修改超链接的 href。待事件冒泡完成、执行默认行为时,浏览器才读取 href 属性,作为最终的结果。 因此,我们只需捕获点击事件,修改超链接地址就可以了。至于是跳转、弹窗、还是被屏蔽,根本不用我们关心。 就那么简单。因为我们是在用户点下去之后才修改,所以浏览器状态栏里,显示的仍是原先 https ! 当然,点过一次之后,再把鼠标放到超链接上,状态栏里显示的就是修改后的了。 为了能继续忽悠,我们在修改 href 之后的下个线程周期里,把它改回来。因为有了一定延时,新页面并不受影响。 var url = link.href; // 保存原始地址link.href = url.replace(‘https://’, ‘http://’); // 暂时换成 http 的setTimeout(function() { link.href = url; // 新页面打开后,还原回来}, 0); 这样,页面里的超链接始终都是正常的 —— 只有用户点下的瞬间,才临时伪装一下。 更多拦截 除了通过超链接,还有其实方式访问页面,我们应尽可能多的进行监控。例如: 表单提交 window.open 弹窗 框架页面 ….. 表单提交 表单提交和超链接非常类似,都具有事件,只是将 click 换成 submit,href 换成 action 而已。 脚本弹窗 函数调用的最简单了,只需一个小钩子即可搞定: var raw_open = window.open;window.open = function(url) {
Read More…