之前介绍了 HTTPS
前端劫持
 的方案,尽管非常有趣。然而现实却并不理想。

其唯一、也是最大的缺陷。就是无法阻止脚本跳转。若是没有这个缺陷,那就非常完美了 —— 当然也就没有必要写这篇文章了。

说究竟,还是由于无法重写 location 这个对象 —— 它是脚本跳转的唯一渠道。虽然也流传一些
Hack 能勉强实现,但终究是不靠谱的。

其实,在近期封稿的 HTML5 标准里,已很明白了 location 的地位
—— Unforgeable。

这是个不幸的消息。只是也是件好事,让我们彻底打消各种偏门邪道的念头。寻求一条全新的出路。

替换明文 URL

上回也提到,能够參考 SSLStrip 那样。把脚本里的 HTTPS URL 全都替换成 HTTP 版本号。就可以满足部分场合。

当然,缺陷也是显而易见的。仅仅要 URL 不是以明文出现 —— 比如通过字符串拼接而成。那就全然无法识别了,终于还是无法避免跳转到 HTTPS 页面上。

这样的情况并不少见。所以我们须要更先进的解决方式。

替换 location

虽然我们无法重写 location。但要山寨一个和 location 功能一样的玩意,还是很easy的。我们仅仅需定义几个 getter 和 setter。就可以模拟出一个功能全然同样的 location2。但怎样将原先的
location 映射过来呢?

这时。后端的作用就发挥出来了。类似替换 HTTPS URL,这次我们仅仅关注脚本里的 location 字符,把它们都改成 location2 —— 于是全部和地址栏相关的读写,都将落到我们的代理上面。之后能做什么。不用说大家也都明确吧。

  • 代理全部的 setter:假设跳转到 HTTPS 就将其拦下,然后降级到 HTTP 版本号上。

  • 代理全部的 getter:假设当前处于降级的页面。我们将返回的路径都还原 HTTPS 字符,就可以骗过协议推断脚本,让那些自检功能彻底失效!

相比之前的 URL 替换。这个方法完美太多 —— URL 是动态创建的很普遍,但 location 不是明文出现的,及其罕见。

除非脚本是加密过的,否则即使用 Uglify 那样的压缩工具。也不会把全局变量给混淆。至于人为刻意去转义它,更是无稽之谈了。

if (window['loc\ation'].protocol != 'https:') {
// ...
}

到此,我们的目标已经明白了:

  • 前端:实现一个 location 代理。

  • 后端:将脚本里出现的 location 替换成代理变量名。

处理外链脚本

尽管替换页面脚本的内容并不困难,但对于外链脚本。那就不容乐观了。

现实中,不少页面外链了 HTTPS 绝对路径 的脚本。这时,我们的中间人就无能为力了。

为了避免这样的情况,我们仍需替换页面里的 HTTPS URL,让中间人能掌控很多其它的资源。

要替换 URL 倒也不难,一个简单的正则就能实现 —— 但既然使用正则。我们面对的仅仅能是字符串了。

然而其实,收到的都是最原始的二进制数据,甚至未必都是 UTF-8 的。

在上一篇文章里。我们为了简单,直接使用二进制的方式注入。但在现在,这种方法显然不可行了。

使用二进制。不仅难以控制。并且非常不严谨。

我们非常难得知匹配到的是独立的字符。还是一个宽字符的部分字节。因此,我们还是得用传统可靠的方式来处理字符串。

处理字集编码

我们得借助字集转换库,比如大名鼎鼎的 iconv,来协助完毕这件事:

  • 首先将二进制数据转换成 UTF-8 字符串

  • 有了标准的字符串。我们的正则就可以顺利运行了

  • 将处理完的字符串,又一次换回先前的编码

虽然这一来一回得折腾两次,性能又得耗费不少,但这仍是必须的。

其实,这个过程也不是想象的那么顺利。有相当多的server,并没有在返回的 Content-Type 里指定编码字集,于是我们仅仅能尝试从页面的 <meta> 中获取。

但这个标签兼容诸多规范,比如过去的:

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=GBK">

以及现在流行的:

<meta charset="GBK" />

虽然通过正则非常easy获取,但用正则的前提还是得先有字符串,于是我们陷入了僵局。

只是好在标签、属性、字集名,基本都是纯 ASCII 字符,所以可先将二进制转成默认的 UTF-8 字符串,从中取出字集信息,然后再进行转码。

处理数据分块

得益于丰富的第三方扩展,上述问题都不难解决。

然而,之前提到过『前端劫持』的一个巨大优势 —— 无需处理全部数据,仅仅需在第一个 chunk 里注入代码就可以。但如今。这项优势面临着严峻的考验。

我们要替换页面里的 HTTPS 资源、location 变量等等。它们会出如今页面的各个位置。

假设我们对每一个 chunk 进行单独过滤、转发,这样会有问题吗?

现实中,未必都是这样理想的 —— 总会有那么一定的几率。替换的keyword正好跨越两个 chunk:

这时候。残缺的首尾都无法匹配到。于是就会出现遗漏。

keyword越长。出现的几率也就越大。对于 URL 这样长的字符串来说,这是一个潜在的隐患。

要完美解决问题,是比較麻烦的。只是有个简单的办法:我们能够扣留下 chunk 末尾部分字符,拼接到下个 chunk 的之前。从而减少遗漏的可能。

当然,假设不考虑用户体验的话,还是收集全然部数据,最后一次性处理,最省事了。

其实还有更好的方案:中间人开启一个缓冲区,将收到数据临时缓存当中。当数据积累到一定量、或者超过多久没有数据时,才開始批量处理缓存队列。

这样就能够避免 频繁的 chunk 上下文处理,同一时候也 不会长时间堵塞用户的响应时间,自然是两全其美的。

这是不是有点类似 TCP nagle 的味道呢。

前端 location 代理

讲完了后端的相关细节,我们继续回到前端的话题上。

实现一个 location 的代理非常easy,只是值得留意的细节倒是不少:

  • location 不仅存在于 window,事实上 document 里也有个同样的。

  • location 对象本身也是能够被赋值的。效果等同于 location.href。

    [PutForwards=href,
    ...]
    已经非常好的解释了)

  • 同理,location 的 toString 返回的也是 href 属性。

  • 假设带有 location2 的脚本被缓存住了,那么用户在没有劫持的页面里,或许就会报错。

    所以还得留一条兼容的后路。

  • ......

仅仅要考虑充分,实现一个 location 的切面还算是比較easy的。

动态脚本劫持

前面谈到替换页面的 HTTPS URL。以确保外链脚本明文传输。

然而现实中,并不是全部脚本都是静态的。现在这个脚本泛滥的时代,动态载入模块是非经常见的事。假设引入的是一个 HTTPS 的脚本,那么我们的中间人又无从下手了。

只是值得庆幸的是,模块拦截不像 location 那样无法实现。现实中。有许多的方法能够拦截动态模块。在之前写的《XSS
前端防火墙 —— 可疑模块拦截》
 一文里。已经具体讨论过各种方法和细节,这里正好派上用场。

其实,除了脚本外,框架页相同也存在这个问题。上一篇文章里。我们採用 CSP 来阻挡 HTTPS 的框架页。但那不过屏蔽。并非真正意义的拦截。

唯独加上现在这套钩子系统。才算一个完好的拦截系统。

演示

说了那么多,真正的核心无非就是改变脚本里的 location 变量而已。其它的一切都仅仅是为了辅助它。

以下我们找几个之前无法成功的站点,试验下这个加强版的劫持工具。

上一篇文章里提到京东登录,就是通过脚本跳转的。

我们首先就拿它測试:

当流量经过中间人代理。页面和脚本里的 location 都变成了我们的变量名。于是之后和地址栏相关的一切。尽在我们的掌控之中了:

注意地址栏里有一个 zh_cn 的标记。那正是 URL 向下转型后的识别暗号。

通过 location2 获取到的一切属性,看起来就像在 HTTPS
页面上一模一样。

即使脚本里有自检功能,也会被我们的虚拟环境所欺骗。

点击登录。自然是成功的。

毕竟,HTTPS 和 HTTP 仅仅是传输上的差异。在应用层上,页面是无法知晓的 —— 除了询问脚本的 location,但它已被我们劫持了。

除了京东的脚本跳转。財付通站点则是通过非主流的 <meta http-equiv="refresh"> 进行的。

好在我们对页面里的 HTTPS URL 都替换了,所以仍然可以跳转到降级后的页面:

值得注意的是。假设是从 QQ 图标里点进来的,那么页面就直接进入 HTTPS 版本号,就不会被劫持了。

但从第三方过来那就听天由命了。

因为一般开发者的思维,是不可能转义 location 这个变量的。因此这套方案差点儿能够通杀全部的安全网站。

当然。外国的站点也是一样的。仅仅要之前没有被 HSTS 所缓存。劫持依然轻松自如。

......

所以,仅仅要发挥无尽的想象,实现一个project化的通用劫持方案。依旧是可行的。

防范措施

假设你是细致看完本文的话,应该早就想到怎样应对了。

其实。因为 JS 具有超强的灵活性,差点儿无法从静态源代码猜測执行时的行为。

因此,仅仅要将涉及 location 相关操作,进行简单的转义混淆,就能躲过中间人的劫持了。毕竟,要在劫持流量的同一时候,还要对脚本进行语法分析,这个代价不免有点大了。

SSLStrip 终极版 —— location 瞒天过海的更多相关文章

  1. 【流量劫持】SSLStrip 终极版 —— location 瞒天过海

    前言 之前介绍了 HTTPS 前端劫持 的方案,虽然很有趣,然而现实却并不理想.其唯一.也是最大的缺陷,就是无法阻止脚本跳转.若是没有这个缺陷,那就非常完美了 -- 当然也就没有必要写这篇文章了. 说 ...

  2. 08重编终极版《东邪西毒:终极版》DVD粤语中字

    1.东邪西毒].Ashes.of.Time.1994.384p.DVDRip.x264.ac3-DTMM.mkv 这个版本最清晰 ,可惜删减了,只有87分钟,粤语,1.4G. 2.东邪西毒(初始版). ...

  3. 阿里官方Java代码规范标准《阿里巴巴Java开发手册 终极版 v1.3.0》

    终极版 v1.3.0 2017年开春之际,阿里诚意献上重磅大礼:<阿里巴巴Java开发手册>,首次公开阿里官方Java代码规范标准.这套Java统一规范标准将有助于提高行业编码规范化水平, ...

  4. python3 购物车 增改查终极版~

    还是先来条NLP再说,快没了,以后想抄还没有... 十一,没有挫败,只有回应讯息 “挫败”只是指出过去的做法得不到预期的效果,是给我们需要改变的信号. “挫败”只是在事情画上句号时才能用上,欲想事情解 ...

  5. RESTful API终极版序列化封装

    urls: from django.conf.urls import url from app01 import views urlpatterns = [ # url(r"comment/ ...

  6. 通过xshell在linux上安装mysql5.7(终极版)

    通过xshell在linux上安装mysql5.7(终极版) 0)通过xshell连接到远程服务器 1)彻底删除原来安装的mysql 首先查看:rpm -qa|grep -i mysql 删除操作(一 ...

  7. 软件工程课堂作业(五)——终极版随机产生四则运算题目(C++)

    一.升级要求:让程序能接受用户输入答案,并判定对错.最后给出总共对/错的数量. 二.设计思想: 1.首先输入答案并判断对错.我想到的是定义两个数组,一个存放用户算的结果,另一个存放正确答案.每输出一道 ...

  8. 阿里正式发布《Java开发手册》终极版!

    摘要: 本文讲的是阿里正式发布<Java开发手册>终极版!,别人都说我们是码农,但我们知道,自己是个艺术家.也许我们不过多在意自己的外表和穿着,但我们不羁的外表下,骨子里追求着代码的美.质 ...

  9. 终极版Servlet——我只能提示您路过别错过

    终极版Servlet 前言:这两天看了SSM框架,本来是想往后继续学的,脑门一转又回来了,不能就这么不声不响的走了,看了这么多天的Servlet,再写最后一篇做个告别吧,这篇起名为终极版,是我现在所能 ...

随机推荐

  1. 高阶函数与接口混入和java匿名类

    高阶函数与接口混入和java匿名类. 高阶函数中的组件(参量)函数相当于面向对象中的混入(接口)类. public abstract class Bird { private String name; ...

  2. CAD参数绘制直线(com接口)

    用户可以在CAD控件视区任意位置绘制直线. 主要用到函数说明: _DMxDrawX::DrawLine 绘制一个直线.详细说明如下: 参数 说明 DOUBLE dX1 直线的开始点x坐标 DOUBLE ...

  3. http和socket

    大多数情况下都是使用Http协议做网络通信的,少数情况下,如扣扣之类的即时通讯,就是用Socket建立长链接 Http一般都是短连接的,即客户端和服务端通讯一次后,服务端就关闭连接 Socket是长连 ...

  4. Dijkstra+set堆优化局部模板

    这是某天2018-10-25写的某题(P1613-luogu)的局部代码,目的是方便自己记忆一些细节,所以这里不过多赘述算法原理或题目 邻接矩阵mapp表示有向图 struct ELE { int i ...

  5. bzoj1174 Toponyms

    给你一个字符集合,你从其中找出一些字符串出来. 希望你找出来的这些字符串的最长公共前缀*字符串的总个数最大化. 第一行给出数字N.N在[2,1000000] 下面N行描述这些字符串,长度不超过2000 ...

  6. 笔试算法题(38):并查集(Union-Find Sets)

    议题:并查集(Union-Find Sets) 分析: 一种树型数据结构,用于处理不相交集合(Disjoint Sets)的合并以及查询:一开始让所有元素独立成树,也就是只有根节点的树:然后根据需要将 ...

  7. Broadcasting

    目录 Broadcasting Key idea How to understand? Why broadcasting? Broadcastable? Broadcast VS Tile Broad ...

  8. stark组件之时间插件(九)

    在模型model中用的都是时间字段DateTimeField字段,在后台处理中可以看到,在生成modelform过程中,继承的是BaseModelForm,而其对时间字段加入了特殊的date_time ...

  9. mysql-5.7.17-winx64免安装配置

    一,下载mysql-5.7.17-winx64.zip 地址:https://dev.mysql.com/downloads/file/?id=467269 二,解压到自己的某个磁盘:data文件夹和 ...

  10. windows下mysql使用实录

    之前密码忘了,卸载重装,配置好环境变量,登录,成功 操作命令可参考http://www.runoob.com/mysql/mysql-tutorial.html 这里只列举了我需要用到的命令 登录:m ...