在AWS Cognito用户池中篡改用户属性

本文探讨了AWS Cognito用户池中用户属性篡改的安全漏洞,展示了攻击者如何利用默认权限设置修改关键属性,从而影响平台业务逻辑,并提供了防护建议和Terraform配置示例。

在AWS Cognito用户池中篡改用户属性

2023年1月24日 - 作者:Francesco Lacerenza, Mohamed Ouad

从前一集说起…

你解决了CloudSecTidbit第1集的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.txtdummy.txt对象。

Tidbit No. 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:AdminInitiateAuthcognito-idp:AdminRespondToAuthChallenge权限的AWS凭证。该操作返回所需的身份验证参数。

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

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

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]

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

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

在AWS Cognito中,应用程序集成(客户端)对用户属性具有默认的读取/写入权限。
下图显示了用户池中新应用程序集成的“属性读取和写入权限”配置。

因此,经过身份验证的用户能够使用访问令牌(JWT)和AWS CLI编辑他们自己的属性。
总之,了解这种行为并在池创建期间正确设置权限非常重要。根据平台逻辑,某些属性应设置为只读,以使内部流信任它们。

对于云安全审计员

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

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

对于开发人员

删除所用用户池(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_attributeswrite_attributes列表中指定了一个属性,则默认的r/w策略将被忽略。
通过这样做,可以严格指定具有读取/写入权限的属性,同时隐式拒绝对未指定属性的权限。

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

动手IaC实验

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

请继续关注下一集!

资源

其他相关帖子:

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