HTTHTTPS前端劫持 新一代流量劫持解决方案PS前端劫持 新一代流量劫持解决方案
前言在之前介绍的流量劫持文章里曾提到一种『HTTPS 向下降级』的方案 —— 将页面中的 HTTPS 超链接全都替换成 HTTP 版本让用户始终以明文的形式进行通信。看到这也许大家都会想到一个经典的中间人攻击工具 —— SSLStrip通过它确实能实现这个效果。不过今天讲解的则是完全不同的思路一种更有效、更先进的解决方案 —— HTTPS 前端劫持。在抓包和分析网络流量时工具如 Sniffmaster 也提供了强大的 HTTPS 抓包能力支持暴力破解和代理抓包无需设备越狱或 root 即可解密 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换成submithref换成action而已。脚本弹窗函数调用的最简单了只需一个小钩子即可搞定var raw_open window.open;window.open function(url) { // FIX: null, case insensitive arguments[0] url.replace(https://, http://); raw_open.apply(this, arguments); }框架页面因为我们把主页面降级成 http 了但里面的框架地址仍是原先的。由于协议不同这会产生跨域问题导致页面无法正常工作。所以我们还要把页面里的框架也都转型成 http 版本确保能和主页面融为一致。但框架和之前的那些不同因为它是自动加载的而且也没有一个即将加载的事件。如果等到框架加载完了再去处理说不定已经开始报跨域错误了。而且还会白白的浪费一次加载流量。因此我们必须让框架一出现还没来让它得及加载就立即替换掉地址。这在过去是个很棘手的问题然而 HTML5 时代给我们带来了新希望 ——MutationEvent。用它即可实时监控页面元素之前也尝试过一些试验。解决了框架页的问题我们就能成功劫持支付宝登录页的账号框 IFrame 了后端配合通过前端的 XSS 脚本我们轻易解决了过去各种棘手的问题。但挑战并未就此结束我们仍面临着众多难题。如何告诉代理尽管在前端上面我们已经避开了各种进入 https 的途径让请求以明文的形式交给代理。但代理又如何决定这个请求用 https 还是 http 转发呢传统的后端劫持之所以能正确转发那是在替换超链接的时候已经做下记录。当出现记录中的请求就走 https 的转发。而我们的劫持在前端并且只发生在点击的一瞬间。即使马上去告诉中间人某个 URL 是 https 的这时也来不及了。告诉中间人是必须的。但我们可以用一个巧妙的方法不必单独发送消息 —— 我们只需在转型后的 URL 里做个小记号就可以了。当代理发现请求的 URL 里有这个记号它自然就懂了直接走 https由于把页面从 https 降级到了 http因此相关请求的referer也变成 http 版了。所以中间人应尽量把 referer 也修正回来避免被 服务器 察觉。隐藏伪装不过在 URL 里加标记的方法也有很大的缺陷。因为页面的 URL 会在地址栏里显示出来所以用户会看见我们的记号。当然我们可以使用一些迷惑性的字符例如?zh_cn、?utf_8?from_baidu等等更好的欺骗用户。当然如果你觉得还是不满意也有办法让这些碍眼标记尽快消失if url has symbol history.replaceState(..., clear_symbol(url) )HTML5 为我们提供了修改地址栏的能力并且无需刷新。这些强悍的功能如今都可以在前端利用起来了。重定向劫持当然光靠前端的劫持还是远远不够的。现实中还有另一种很常见的方式那就是重定向到安全页面。仔细回想下平时我们是怎样进入想上的网站的。例如支付宝除非你有收藏否则就得自己敲入 www.alipay.com 或 www.zhifubao.com当你回车进入时浏览器又如何知道这是个 HTTPS 的网站呢显然第一个请求仍是普通的 HTTP 协议。当然这个 HTTP 版的支付宝的确存在它的唯一功能就将用户重定向到 HTTPS 版本。当我们的中间人一旦发现有重定向到 HTTPS 网站的当然不希望用户走这条不受自己控制的路。于是拦下这个重定向然后以 HTTPS 的方式获取重定向后的内容最后再以 HTTP 明文的方式回复给用户。因此在用户看来始终处于 HTTP 网站上。不过如今的 Web 里增加一个新的安全标准HTTP Strict Transport Security。如果客户端收到这个头部之后一段时间内访问该站点就始终通过 HTTPS 的方式。所以我们的中间人一旦发现有这个字段就得果断将其删除。当然用户直接敲网址的并不常见。大多都是搜索引擎然后直接从第一个结果里进来了。比较悲剧的是国内的搜索引擎几乎都是 HTTP 的。在用户访问搜索页面的时候我们的 XSS 早已潜伏在其中了因此从中点出来的任何一条结果都是进不到官方的 HTTPS 里的除了搜索页面不少类似 hao123 之类的网址大全大多也未开启 HTTPS。因此从中导流的网站都面临着被中间人劫持的风险。防范措施介绍了攻击方法接着讲解防御措施。脚本跳转事实上无论是前端劫持还是后端过滤仍有不少的网站无法成功。例如京东的登录它是通过脚本跳转到 HTTPS 地址的。而浏览器的location是个及其特殊的属性它可以被屏蔽但无法被重写。因此我们难以控制页面的跳转情况。如果非要劫持京东页面我们只能使用白名单的方式特殊对待该站点。但这样就大幅增加了攻击成本。混淆明文当然不难发现京东的登录脚本里URL 是以最直白的明文出现的。所以我们利用 SSLStrip 的方式对脚本里的https://的文本进行替换也能起到一定的作用毕竟大多脚本都对此毫无防备。但对于稍微复杂一点的脚本例如通过字符串拼接而成的 URL那么就难以实施了。所以在安全需要较高的场合不妨把一些重要的地址进行简单的处理中间人就无法使用通用的方式来攻击。而必须针对站点进行特殊对待从而提高攻击成本。尽可能多的 HSTS之前提到HSTS头。只要这个字段出现过一次浏览器在很长时间里都会只用 HTTPS 访问站点。因此我们尽可能多的开启 HSTS。现实中的劫持并非都是 100% 成功的上述提到使用脚本跳转很容易出现遗漏。所以只要逮住用户一次遗漏HSTS 就可以让之后的页面降级彻底失效了。作者DarkModeDev链接https://www.imooc.com/article/391946来源慕课网本文原创发布于慕课网 转载请注明出处谢谢合作