Categories
程式開發

使用Websockets窃取开发人员的秘密


这个故事讲的是一种很复杂,但也不是很有用的方法,用来从开发最机密项目的JavaScript开发人员那里窃取代码。

最近网上出现了两篇文章,谈的是一些网站滥用Websocket功能来扫描用户计算机的端口

使用Websockets窃取开发人员的秘密 1
这些技术之所以能起作用,是因为浏览器在缺乏很多防护措施的情况下,允许来自公开源的Websocket开启到localhost的Websocket连接。

于是我开始沿着这个方向进一步思考下去。我知道很多流行的JavaScript框架在开发中使用了Websocket,用来在内容更改时自动重载页面。那么恶意网站能否窃听这些流量,并找出开发人员是何时保存代码的呢?

现实比我想的还要糟糕。

方法

  1. 创建一个受欢迎的前端开发网站,或将恶意软件注入到前端开发人员经常访问的站点中。比如说:http://frontend-overflowstack.com/
  2. 在这个页面上添加一些代码,尝试打开与普通端口的Websockets连接(扫描1万个端口大约需要一秒钟的时间,因此这里请随意操作)
  3. 如果页面打开了某个连接,请使其保持打开状态,然后将收到的所有消息转发到你的恶意数据库。
  4. 获利

这种法子行得通吗?

我在这里托管了一个非常简单的页面:http://frontend-overflowstack.com/。页面加载时,它会尝试将一个Websocket连接到访问者计算机上,从2000到10000全部试一遍(除去Firefox不允许的一些端口)。如果某个端口成功连接,它会侦听该端口并输出自己收到的所有消息。这个页面不会保存或传输任何捕获的数据,只是会暂时显示在页面上

如果这个页面显示了任何输出结果,那就意味着一个真正的恶意站点可以轻松地将自己捕获的输出发送到黑客指定的任何服务器上。

生成数据

为了测试这个概念,我们需要一个使用了热重载的简单Web服务器。下面是我能想到的最简单方法:

var express = require('express')
var http = require('http')
var path = require('path')
var reload = require('reload');
var app = express()
 
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, 'public', 'test.html'));
})
var server = http.createServer(app)
 
reload(app).then(function (reloadReturned) {
  server.listen(3000, function () {});
setInterval(reloadReturned.reload, 5000);
});

运行时,它将在端口3000上启动一个服务器,在端口9856上启动一个Websocket服务器,并发送一条消息:每5秒reload到连接的任何Websocket客户端上。
如果我们启动嗅探器站点,则会显示以下内容:

使用Websockets窃取开发人员的秘密 2
也就是说,frontend-overflowstack.com直接窃听了本地开发服务器发送到我本地浏览器的重载消息。

在这个阶段,黑客可以轻松得出我们网站的访问者对其本地JavaScript代码更改的次数,但是我们可以借此获得更多信息吗?

扩大漏洞

如今,大多数前端开发工作似乎都在使用React,并且往往会运行webpack-dev服务器。这个服务器提供了更好用的Websocket接口。

它会通过自己的Websocket共享更多数据(稍微有趣一些)。只需调用create-react-app即可:

$ npx create-react-app test
$ cd test/
$ npm start

如果我们运行此命令,然后再次查看我们的恶意站点:
使用Websockets窃取开发人员的秘密 3
马上就有了更多数据,我们收到了哈希和状态消息,还有很多无用信息。

但当开发人员输入错误时会发生什么呢?Webpack开发服务器会通过其Websocket连接,尝试将大量调试信息和堆栈信息发送到开发人员的屏幕上。

不幸的是我们的邪恶站点也可以看到这些信息:

使用Websockets窃取开发人员的秘密 4
现在事态愈加严重。我们得到了代码段、文件路径、位置之类各种有用的信息。

如果开发人员在包含有用数据的代码行上意外输入了错误代码,情况就更糟了:

使用Websockets窃取开发人员的秘密 5
现在,我们已经获得了这位开发人员的AWS Dev凭证的副本。赶快,拿他的服务器去挖矿吧!

“攻击”的剖析

任何技术设计都需要某种形式的图表才称得上完整。下面是这种攻击机制的图形说明:

使用Websockets窃取开发人员的秘密 6
(为简化这张图,我省略了运行的本地Web服务器,并假装Websocket服务器直接来自编辑器内部)

某些浏览器标签上的恶意网页会以静默方式连接到用户计算机上开启的Websockets。当用户通过这个socket发送敏感数据时(从本应只通过本地通道通信的进程来发送),网站就可以收到消息数据,并将其转发到任意外部数据库。

威胁评估

局限因素

认真来说,这种攻击途径是非常狭窄的。你必须诱使不知情的用户访问你的网站,并在他们开发JS代码时依旧开着你的页面。

然后你必须等待他们出现编码错误,才能从中收集少量数据,找机会从这些数据中获取利益。

风险因素

但我们知道,很多站点都在使用Websocket端口扫描技术,开发人员不一定对此有足够的了解。鉴于JS工具链倾向于使用少量知名端口,因此编写脚本来巧妙地泄露React开发流量并不是特别困难。

想象一下,为Twitbook工作的内部开发人员只是在编辑器中按了“保存”按钮,就会将访问令牌或内部服务器地址泄露给了恶意方。

值得关注的是,在开发人员的一般认知中,在代码编辑器中按“保存”按钮应该完全不会将数据泄漏到第三方Web服务。而本文介绍的这种攻击机制意味着,这种数据泄漏的风险还是存在一些的。

对策

我之所以采用了这种尝试拦截JavaScript热重载机制的方法,是因为我熟悉的唯一一种Websocket通用用法就是它了。

Discord也使用了Websockets,但初看起来那里没什么漏洞可钻,因为这个通道在设计时就考虑到了公共互联网环境。

令人担忧的是,仅此一种单向通信通道的简单用例,就可以将大量潜在信息暴露给恶意网站。

鉴于此,Websocket的其他用途(针对本地使用的数据)可能也会存在类似的漏洞。

我建议,webpack-dev服务器应该进行一些身份验证,或者为热重载功能选择其他一些浏览器通信通道(我相信这部分工作已经在计划中了,不过是出于其他原因)。

浏览器/Web标准竟然会以这种方式来实现Websockets的策略,这实在很让人惊讶。结果开发人员只是在本地编写代码,就可能会无意中将敏感信息泄漏到公共互联网上,

我希望人们针对这个问题能开发出修复措施,在浏览器中增加额外的安全控制策略。

原文链接:

https://medium.com/@stestagg/stealing-secrets-from-developers-using-websockets-254f98d577a0