AWS Cognito用户池中的用户属性篡改漏洞分析

本文详细分析了AWS Cognito用户池中存在的用户属性篡改漏洞,攻击者可通过访问令牌直接修改自定义属性,导致第三方平台账户数据被窃取,并提供了Terraform配置修复方案。

从上一集说起… 你解决了CloudSecTidbit第一期的IaC实验吗?

解决方案

data-import CloudSecTidbit的挑战主要是读取内部存储桶的内容。前端Web应用程序使用目标存储桶来存储应用的徽标。

通过调用/variable端点返回存储桶名称给客户端:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$.ajax({
    type: 'GET',
    url: '/variable',
    dataType: 'json',
    success: function (data) {
        let source_internal = `https://${data}.s3.amazonaws.com/public-stuff/logo.png?${Math.random()}`;
        $(".logo_image").attr("src", source_internal);
    },
    error: function (jqXHR, status, err) {
        alert("Error getting variable name");
    }
});

服务器将返回类似内容: "data-internal-private-20220705153355922300000001"

现在架构应该很清晰了。让我们使用数据导入功能尝试泄露data-internal-private S3存储桶的内容:

然后,通过访问Data Gallery部分,你将看到存储在内部存储桶中的keys.txt和dummy.txt对象。

知识点2 - 在AWS Cognito用户池中篡改用户属性

Amazon Web Services提供了一个完整的解决方案来为Web和移动应用程序添加用户注册、登录和访问控制:Cognito。让我们先总体讨论一下该服务。

从AWS Cognito欢迎页面:

“使用Amazon Cognito用户池API,您可以创建用户池来管理目录和用户。您可以对用户进行身份验证以获取与用户身份和访问策略相关的令牌。”

Amazon Cognito将用户配置文件属性收集到称为池的目录中,应用程序使用这些池来处理所有与身份验证相关的任务。

池类型

Amazon Cognito的两个主要组件是:

  • 用户池:为应用程序用户提供注册和登录选项,以及为每个用户关联属性
  • 身份池:为用户提供访问其他AWS服务(如DynamoDB或Amazon S3)的可能性

通过用户池,用户可以通过Amazon Cognito、OAuth2和SAML身份提供商登录应用程序。每个用户都有一个配置文件,应用程序可以通过软件开发工具包(SDK)访问。

用户属性

用户属性是用于描述单个用户的信息片段,例如姓名、电子邮件地址和电话号码。新的用户池具有一组默认的标准属性。还可以添加自定义属性以满足自定义需求。

应用程序客户端和身份验证

应用程序是用户池中的一个实体,具有调用管理操作API的权限,例如用于用户注册、登录和忘记密码的API。

为了调用操作API,需要应用程序客户端ID和可选的客户端密钥。可以为单个用户池创建多个应用程序集成,但通常,应用程序客户端对应于应用程序的平台。

用户可以使用Cognito以不同方式进行身份验证,但主要选项是:

  • 客户端身份验证流程 - 在客户端应用程序中使用,直接从池获取有效的会话令牌(JWT)
  • 服务器端身份验证流程 - 在服务器端应用程序中使用,使用经过身份验证的服务器端API用于Amazon Cognito用户池。服务器端应用程序调用AdminInitiateAuth API操作。此操作需要具有包括cognito-idp:AdminInitiateAuth和cognito-idp:AdminRespondToAuthChallenge权限的AWS凭证。该操作返回所需的身份验证参数

在这两种情况下,最终用户都应收到生成的JSON Web令牌。

在初步了解AWS SDK凭证后,我们可以直接进入本期的案例。

AWS Cognito用户池中的无限制用户属性写入 - 第三方用户映射案例

在这个案例中,我们将重点分析在一个使用AWS Cognito的Web平台中发现的漏洞。

该平台使用Cognito管理用户,并将他们映射到第三方平台X_platform中的账户,该平台与所提供的服务紧密相连。

特别是,用户能够连接他们的X_platform账户,并允许平台获取他们在X_platform中的数据以供后续使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "sub": "cf9..[REDACTED]",
  "device_key": "us-east-1_ab..[REDACTED]",
  "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_..[REDACTED]",
  "client_id": "9..[REDACTED]",
  "origin_jti": "ab..[REDACTED]",
  "event_id": "d..[REDACTED]",
  "token_use": "access",
  "scope": "aws.cognito.signin.user.admin",
  "auth_time": [REDACTED],
  "exp": [REDACTED],
  "iat": [REDACTED],
  "jti": "3b..[REDACTED]",
  "username": "[REDACTED]"
}

在AWS Cognito中,用户令牌允许调用所有可以使用访问令牌单独访问的用户池API。允许的API定义可以在这里找到。

如果API调用的请求语法包含参数"AccessToken": "string",那么它允许用户使用先前检查的JWT修改他们自己的UserPool条目。

上述设计本身并不代表漏洞,但如果后端使用这些属性来应用内部平台逻辑,让用户能够编辑他们自己的用户属性可能会导致严重影响。

使用AWS CLI获取池中的用户关联数据:

 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
$ aws cognito-idp get-user --region us-east-1 --access-token eyJra..[REDACTED SESSION JWT]

{
    "Username": "[REDACTED]",
    "UserAttributes": [
        {
            "Name": "sub",
            "Value": "cf915…[REDACTED]"
        },
        {
            "Name": "email_verified",
            "Value": "true"
        },
        {
            "Name": "name",
            "Value": "[REDACTED]"
        },
        {
            "Name": "custom:X_platform_user_id",
            "Value": "[REDACTED ID]"
        },
        {
            "Name": "email",
            "Value": "[REDACTED]"
        }
    ]
}

简单推论

在找到X_platform_user_id用户池属性后,很明显它存在有特定目的。实际上,平台正在获取该属性,将其用作主键来查询内部数据库中的关联refresh_token。

尝试欺骗该属性非常简单:

1
$ aws --region us-east-1 cognito-idp update-user-attributes --user-attributes "Name=custom:X_platform_user_id,Value=[ANOTHER REDACTED ID]" --access-token eyJra..[REDACTED SESSION JWT]

属性编辑成功,其他用户的数据开始流入攻击者的账户。平台信任该属性为不可变的,并使用它来检索refresh_token,该令牌用于在UI中获取和显示来自X_platform的数据。

故事要点 - 用户属性的默认读写权限

在AWS Cognito中,应用程序集成(客户端)对用户属性具有默认的读写权限。

下图显示了用户池中新应用程序集成的"属性读写权限"配置。

因此,经过身份验证的用户能够使用访问令牌(JWT)和AWS CLI编辑自己的属性。

总之,了解这种行为并在池创建期间正确设置权限非常重要。根据平台逻辑,某些属性应设置为只读,以使它们在内部流程中可信。

对于云安全审计人员

在审计云驱动的Web平台时,查找由AWS Cognito颁发的JWT,然后回答以下问题:

  • 哪些用户属性与用户池关联?
  • 哪些属性可以直接通过AWS CLI使用JWT进行编辑?
  • 在可编辑的属性中,平台是否信任这些声明?
  • 用于什么内部逻辑或功能流程?
  • 编辑如何影响业务逻辑?

对于开发人员

删除所用用户池(AWS Cognito)中应用程序集成中每个平台关键用户属性的写权限。通过删除它,用户将无法使用其访问令牌执行属性更新。

更新将仅通过管理操作(如admin-update-user-attributes方法)可能,这需要AWS凭证。

+1修复提示:为避免手动操作,在IaC中应用r/w配置并正确部署基础设施。Terraform示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
resource "aws_cognito_user_pool" "my_pool" {
  name = "my_pool"
}

...

resource "aws_cognito_user_pool" "pool" {
  name = "pool"
}

resource "aws_cognito_user_pool_client" "client" {
  name = "client"

  user_pool_id = aws_cognito_user_pool.pool.id
  read_attributes = ["email"]
  write_attributes = ["email"]
}

给定的Terraform示例文件将创建一个池,其中客户端将仅对"email"属性具有读写权限。实际上,如果至少在read_attributes或write_attributes列表中指定了一个属性,则默认的r/w策略将被忽略。

通过这样做,可以严格指定具有读写权限的属性,同时隐式拒绝未指定属性的权限。

请确保在Cognito上下文中正确处理电子邮件和电话号码验证。由于它们可能包含未经验证的值,请记住应用RequireAttributesVerifiedBeforeUpdate参数。

动手IaC实验室

正如本系列介绍中所承诺的,我们开发了一个Terraform(IaC)实验室,用于部署易受攻击的虚拟应用程序并利用该漏洞进行实验:https://github.com/doyensec/cloudsec-tidbits/

敬请期待下一集!

资源

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计