使用WebAssembly和Go实现浏览器本地端口扫描

本文详细介绍了如何利用WebAssembly和Go语言在浏览器环境中实现本地端口扫描,探讨了浏览器作为localhost网关的安全风险,并演示了通过WASM运行时扫描本地开放端口的技术方案。

浏览器是localhost网关:使用WebAssembly和Go进行客户端端口扫描

网站倾向于从浏览器扫描用户的开放端口,以更好地识别新用户/回访用户。

浏览器能否滥用’localhost’? 能否通过WebAssembly实现? 实时演示可在 http://ports.shhttps://ports.sh 查看, 代码可在 https://github.com/avilum/portsscan 获取, 欢迎贡献代码!

在本文中,我将演示如何滥用浏览器攻击localhost服务——渗透组织或从浏览器运行远程代码。

当我们访问网页时,每个人都有一个独特的指纹,这不是什么秘密。客户端指纹识别帮助网站在众多其他网站上跟踪我们的活动。

指纹是关于您设备、浏览器、屏幕大小、IP地址和许多其他变量的许多因素的组合。当组合在一起时,它们使我们很容易被网站识别。

关于客户端代码的一句话

像eBay这样的网站在我们计算机上无休止地运行代码。 它们过去只用JavaScript来做这件事。 多年来,前端技术不断发展——Javascript(ECMAScript)、TypeScript、Deno(用rust编写的安全快速的javascript运行时)等等…

浏览器的Javascript API一直被恶意软件滥用,因为它是浏览器支持的唯一语言。随着近年来正在开发的下一代Web技术,如WebAssembly,下一代恶意软件可能更加复杂。

WebAssembly(WASM)运行时允许将语言编译成二进制代码,当访问网页时,浏览器的WebAssembly运行时可以使用这些代码在您的浏览器中执行低级代码。除了更快(编译代码通常比JS快,但不总是),WASM将编程语言解耦,专注于"做什么"。

WebAssembly的运行时非常棒——它为浏览器带来了许多新的API和功能,但没有免费的午餐——WASM是安全研究人员和黑客的一个巨大目标。毕竟,尽管它是公开的,但WASM有大量代码只有很少的眼睛审查过。

这就是为什么许多新语言如Rust、Go和Deno,为WebAssembly作为目标架构/运行时提供开箱即用的支持。

到目前为止,您可以用Go、Rust等编写代码,而不是使用javascript衍生的语言。因此,系统编程语言在开发人员中变得广泛适应,他们希望有能力达到我们曾经视为"跨平台"之外的东西。

Python现在不那么"跨平台"了,嗯?(开个玩笑——您可能会发现https://pyodide.org/en/stable/console.html 有趣)。

研究WebAssembly运行时(使用Go)

当用户访问我的网站时,从浏览器上下文映射主机上的开放端口(活动、监听服务)有多容易?我可以用低级语言做到吗?

作为网站所有者,假设我想识别用户是否是开发人员。 端口扫描技术每秒钟都在发现资产和服务器。“开放端口"字面上指的是绑定到NIC设备特定端口的服务器。一些服务必须手动定义,但许多操作系统在启动时运行服务,暴露许多应用程序API,可在localhost上使用,如IPC、SMB/Samba、SSH、SMTP、FTP等。

多亏了这一点,找到容易受到1天漏洞(公开但未披露)攻击的资产非常容易。这只是谁先到那里的问题,以及是否报告(和修复)。

自从多年前第一次听说WASM以来,我一直相信它。我开始玩它。我选择Go是因为它简单的Net/Socket和HTTP标准库API。顺便说一下,Rust也有大量的WebAssembly编译支持开箱即用。

理解流程

用户访问网页 现在,用户的浏览器将初始化WebAssembly运行时。 然后,它将自动运行我的Go端口扫描器,该扫描器已编译成WebAssembly二进制文件。

开始:编写一个(不那么简单的)端口扫描器

我使用Go的’syscalls/js’绑定遵循Javascript API。 我发现浏览器喜欢我使用HTTP Go API,它与(可能没有完全实现的)’net’ Go API不太配合。

浏览器中尚未支持WebAssembly原始TCP会话和UDP。社区也在研究新的WASI标准——最终将要求浏览器实现原始TCP/UDP会话,有一天。

在尝试了几种滥用WebAssembly原始TCP会话的方法后,接收原始TCP响应,我理解使用原生TCP会话扫描编程不会工作。Chrome代理(有时阻止)WebAssembly请求和响应出于安全原因(如CORS和不安全端口)。

让我们定义端口扫描器:

然后,我们将定义端口扫描功能:

Go的"http” API有一些很大的好处,比如用于HTTP跟踪的’GotFirstResponseByte’处理程序。我希望它工作,但它没有——想象一下那会有多容易。

最好利用Go著名的"http"包,毕竟——浏览器都是关于应用程序的。使用像HTTP这样的应用协议应该更容易。

理解响应

如何使用高级HTTP会话一致地扫描端口? 获取响应是一回事。理解它们并正确分类它们是一个挑战,因为我们不能看到原始响应。端口扫描器通常使用原始数据包和会话分类网络。在WebAssembly运行时内部,在撰写本文时这是不可能的。

我们可以积极分割以下响应:

  • 连接被拒绝(端口关闭)
  • 超时(端口可能开放或关闭)
  • HTTP响应(开放——另一边有一个有效的HTTP服务器!)

我发现相对较小的超时非常可靠。

随着进展,我遇到了更多问题。浏览器阻止缺少CORS头的有效HTTP响应,将其呈现为通用"获取错误",掩盖错误的真实原因,并将请求标记为"不安全"。浏览器甚至没有将错误内容转发到WASM运行时——它只是失败,没有进一步细节。

迭代常见错误。将它们视为"开放端口"。

CORS怎么样?

localhost HTTP服务经常缺少"Cross-Origin-****“头。 默认提供跨源(与window.location不同的主机+端口组合)保护,因此没有这些头的HTTP请求将失败。在WebAssembly上下文中,您无法知道请求是否因CORS失败。

我必须以某种方式克服这一点, 在经典JS中,人们只需在fetch()请求中添加’no-cors’模式。在研究了一点,并思考了一种覆盖JS的fetch() API调用的方法后,我找到了一个配方——一个非常简单的——效果很好!

我从未想过在我的请求中添加这样的HTTP头。这是一种使用Go语言修改浏览器行为(系统调用)的奇怪方式。

所以CORS不再是演示的问题,但这并不意味着它将在明确指定’Access-Control-Allow-Origin’头的网站上工作。

我们无法解决这种情况。尽管如此,世界上有大量网站将成功运行此WASM代码。

支持TLS/SSL服务的端口

没有SSL握手,我们"跳过"浏览器默认激活的许多SSL安全功能。这帮助我找到任何TCP开放端口,而不仅仅是接受SSL传输的服务。

我们必须记住,WebAssembly运行时的TCP堆栈由Chrome自己的TCP堆栈代理。如果证书不匹配,浏览器终止我们的SSL请求,而不指定原因——它将看起来像典型的"连接被拒绝”。SSL增加了复杂性和安全功能,因此我继续进行纯文本HTTP。

定义启动功能:

请注意我使用syscalls/js库注入的JS DOM元素,这使得可以使用Go代码修改DOM。我对简单性感到惊讶。

编译代码:

定义一个加载WASM的网页 现在让我们看看客户端的计算机。哪些端口当前正在监听(例如"开放")?

注意wasm_exec.js脚本标签,它是WebAssembly Go运行时绑定。

启动本地演示:

python3 -m http.server 5000

访问时运行本地端口扫描: 访问者启动时看到的(index.html)——HTML本身在Go加载时注入!多酷啊?

让我们看看访问者的网络数据包 网络数据包在localhost(环回)网络接口上捕获。数据包在左侧编号:数据包10-20,是index.html请求和响应,包括脚本。数据包22之后,浏览器开始端口扫描。它发射(TCP SYN)数据包,并接收(RST, ACK)数据包响应,意味着端口关闭。

现在让我们看一个开放端口指示器。 如果套接字接收HTTP超时,而没有收到ConnectionRefused错误,必须有东西在该端口上监听(通常视为’过滤’)接受连接,返回重置/确认响应。

注意有效的HTTP/TCP响应(蓝色/紫色)。有人在那边监听!

并将所有内容整合在一起:

通过浏览器的WebAssembly运行时在访问者上运行端口扫描的HTML页面

将结果与实际情况比较

我使用netstat和nmap验证了我所看到的。 我期望我的结果相同,它们确实是。

NetStat输出(当前操作系统上监听的开放端口)与NMap输出(从攻击者角度TCP扫描这些端口)的比较。我在WASM端口扫描器中找到的每个端口,都得到了这些结果的支持。

Localhost毕竟不是本地的,而是远程的

在今天配置或处理个人计算机时,我们将Localhost视为"安全"或"门控"环境。 似乎曾经攻击localhost服务要困难得多。 我们安装的持久应用程序经常在localhost上分配和监听端口。 操作系统倾向于在启动时在localhost网络接口上打开端口以工作。无论是Windows、Mac还是Linux——您的计算机在localhost上监听某些东西。如我所展示的——Javascript或WebAssembly应用程序可以轻松扫描这些localhost服务。它们也可以滥用它们。

场景1:通过Linux客户端上的RPC漏洞拒绝服务,使用单次访问

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8779 RPC-BOMB是一个示例漏洞,使攻击者能够通过过时的Linux内置RPC服务(rpcbind)使用恶意TCP数据包对操作系统造成拒绝服务攻击。

想象一下:您访问一个网站。 一秒钟后——您的计算机卡住或重置,销毁您最新的未保存工作。

  1. 查找该易受攻击端口是否开放(111)
  2. 向localhost:111发送特制TCP数据包
  3. 您已成功关闭访问者的计算机。

场景2:Windows打印后台处理程序远程代码执行漏洞(CVE-2021–34527):

当Windows打印后台处理程序服务不当执行特权文件操作时,存在远程代码执行漏洞。成功利用此漏洞的攻击者可以使用SYSTEM权限运行任意代码。然后攻击者可以安装程序;查看、更改或删除数据;或创建具有完全用户权限的新帐户。

由于浏览器对localhost、0.0.0.0、192.168.1.1等的访问,此漏洞可用于对抗数百万用户,利用不同设备。

我们许多人使用Windows,此易受攻击服务在启动时运行。绑定在0.0.0.0上,可通过网络访问,黑客可以使用此漏洞渗透组织。

开发人员(如我自己)经常在localhost上运行本地服务器/容器。 我也经常忘记关闭它们(例如,当运行具有restart=always的docker容器时)。除了利用漏洞,开发人员可以被识别(例如通过StackOverflow/Linkedin/Facebook)以了解我们在开发中使用哪些技术。

依我拙见,随着时间的推移,浏览器WASM攻击面将被更多滥用,因为更多功能将被实现。

在本文中,我演示了网页如何关于浏览器的安全功能与用户的本地主机网络上的服务通信和映射。它可以用许多编译成Web程序集的语言完成,尽管原始TCP/UDP会话的使用在今天还不可能。

我举了一个场景示例,其中Linux主机可以通过从浏览器访问网页经历DOS——以及如何也可以使用Microsoft的新漏洞CVE-2021–34527。

WASI很棒。 WebAssembly很棒。 浏览器并不那么棒——随着时间的推移,它们将始终对我们所有人构成严重风险和攻击向量,因为它们变成操作系统,支持越来越多的WASI规范功能。

实时演示

使用WebAssembly的实时演示: http://ports.shhttps://ports.sh http://ports.sh 将找到localhost上的所有TCP服务。 https://ports.sh 将只找到localhost上的HTTPS服务。

代码

代码可在 https://github.com/avilum/portsscan 获取,欢迎贡献。

#浏览器 #端口扫描 #网络安全 #Webassembly #Chrome

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计