对象存储中,上传文件都是以对象形式存储在云端,既不会存在可执行文件,又不会因为同站文件存在XSS,所以现在的大趋势下都是将自己所有的静态文件(图片、js、视频等)搬迁至对象存储上,以为就肯定安全了,殊不知搬迁到文件存储上以后,不安全的配置,也会带来很多的安全问题。

基本概念
对象存储作为分布式存储服务,最主要为用户提供了文件上传、文件下载、文件管理(处理)等操作接口,国内常见的几家云厂商的对象存储文档列举如下:

腾讯COS https://cloud.tencent.com/document/product/436/7751阿里OSS https://help.aliyun.com/document_detail/31947.html百度BOS https://cloud.baidu.com/doc/BOS/index.html华为OBS https://support.huaweicloud.com/api-obs/zh-cn_topic_0031051947.html
一般来说,云厂商会提供类似
https://bucket.s3-china-region.aaa.com/?ak=xxxx&sgin=xxxx&time=16xxxx
的类AWS s3 签名URL形式提供下载、上传、管理API,具体参数意义如下:
  • AK用于控制用户权限
  • Sgin用于验证签名,一般的云厂商都会把AK、domain、URI、Query、Header等请求特征、身份特征以及时间戳拼接在一起,然后使用SK做为盐值进行hash,来生成最后的Sgin,用于校验此数据包是否合法。
  • time是时间戳,用户标识此URL过期时间

上传时的安全问题
  • AK / SK前端硬编码
    在对象存储的SDK中,一般都会存在javascript的SDK,也就是支持用户前端上传的
    一般来说,在安全的配置下,前端上传会使用子用户、或者临时密钥(STS token)的方式来进行,但是由于某些不怕死的开发,前端js文件、APK文件硬编码AK / SK的情况还是司空见惯,一般来说只需要在burpsuite里面搜索对应云AK的名字,例如
AccessKeySecret
SecretKey
等。

    获取用户AK、SK以后,不仅是可以获取此bucket权限,也可以获取此AK所在的权限范围内的所有云API的操作权限。

  • 任意文件覆盖
    在上传的时候,有的业务会使用如下逻辑进行上传:

  • 首先通过文件时间戳生成一个随机文件名
  • 通过此文件名在后端获取文件名PUT签名
  • 通过该PUT签名上传

    这个时候,由于上传的路径是我们控制,所以我们可以通过修改签名路径的形式,来进行签名,从而去覆盖此存储桶下的任意文件:

    如果静态资源都存储在一个bucket的话,可以达到覆盖任意文件,如果没有办法覆盖(后端校验了路径的合法性),在对象存储的也页面我们都可以看到,对象存储是允许配置独立域名。


    可能你上传的时候走的aliyuncs.com域名,但是发现,下载文件的时候域名变成了qiye.com,这时也可以尝试XSS攻击。


  • STS token
    由于前端使用SDK上传,会减少很多前后端交互API的编写,所以很多开发总是会选择前端上传,所以云厂商开发了STS token的模式:

    由于STS token是对针对API接口而不是大范围的云产品的,有的时候在存在AK、SK、STS都返回以后,上传有限制,只允许在特定目录下传文件的时候,不妨试一试带上STS访问ECS、SLB等其他同样支持STS的服务,对于AWS S3标准的STS,我们还可以通过BASE64解码来获取对应的权限

  • PUT ACL接管存储桶
    这是腾讯云的PUT ACL的解释

    但是上次我们要如何去进行对ACL进行操作呢?如果是前端请求签名,并且前端上传的话,如果后端对路径没有做限制,我们就可以通过:

GET/get_put_sgin?uri=/ HTTP/1.1Host: 127.0.0.1:5000Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: close
    获取根目录来进行PUT ACL的写入操作
PUT /?policy&sgin HTTP/1.1Host: ceshicos-1253712656.cos.ap-nanjing.myqcloud.comAccept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type:application/jsonContent-Length: 381{"Statement": [ {"Principal": {"qcs": ["qcs::cam::uin/UIN:uin/UIN" ] },"Effect": "allow","Action": ["name/cos:GetBucket","name/cos:GetObject","name/cos:PutObject" ],"Resource": ["qcs::cos:ap-nanjing:uid/1253712656:ceshicos-1253712656/*" ] }],"version": "2.0"}

    来对根目录进行ACL的写入,但是要注意的是
PUT bucket写入是一个覆盖操作,会覆盖原有业务的ACL导致业务无此bucket权限,导致业务中断
。所以如果发现此问题不要贸然测试。


下载时的安全问题
  • 读取对象存储元信息
    对象存储为什么可以保证存储文件的安全?在私有读的存储桶里,读取任意文件都需要授权签名,签名过期后就无法读取,这样就会导致一张图片必须每次读的时候都去请求权限,在请求权限时,业务最常用的接口就是:

GET/download?uri=this_is_mine HTTP/1.1Host: 127.0.0.1:5000Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: close
    服务端会返回签名后的链接给我们访问文件,但是如果我们的uri设置为空会怎么样呢?


    很明显,对我们的根目录做了签名,访问的话,我们就可以获取对象存储的管理节点信息:

使用对应key即可获取任意文件(我这里是个空的存储桶)。

其他
    除了以上所说的,其他的特性也可能会存在很多安全问题,例如
  • 镜像回源 SSRF
  • 目录形式bucket回源

  • callback接口泄漏
  • SDK SSRF

  • 参数注入
  • 日志泄漏
    等等,玩法很多,大家可以自行摸索。

结语
    对象存储确实让常规的文件上传漏洞利用价值降低,但是又由于开发的错误应用导致新型漏洞也会在对象存储中,对于这些漏洞,建议各位开发严格按照文档部署,对硬编码key一定要听从建议使用子账户,能避免大多数问题。

继续阅读
阅读原文