【技术解读】【CloudSec】A deep dive into AWS S3 access controls
A deep dive into AWS S3 access controls 当时是 PortSwigger安全社区评选的 2017年Web hacking techniques 的Top 7。
作者对AWS S3桶的访问控制进行的研究,指出了多个层面上配置访问控制时容易出现的不当配置以及会导致的安全风险。
尽管在今天看来,文章中提到的点早已不新鲜,但是作为如今依旧是云安全场景下常见的风险点,还是值得好好温习下的。
S3桶里的文件,可以配置成私有访问(访问Url需要带上签名);
也可以通过S3的ACL访问控制进行具体的读、写权限的配置使文件可以被公开访问。
如何识别S3存储桶
1、通过HTTP响应头判断
查看http响应里的Server
响应头为:AmazonS3
.
2、通过错误响应信息判断
可以访问一个随机的不存在的url,看是否返回经典的S3 404页面(一般包含NoSuchKey
)或403页面(一般包含Access Denied
),如:
3、通过DNS记录来判断
关于这种方式,文章并没有具体阐述,只是一句话带过。不过我询问了GPT,它给了我一个答案:
简单来说,就是通过域名的CNAME
来识别。
或者其实有时候网站后端返回给前端具体的文件路径url的域名就包含了s3这种字眼。
4、通过访问根url来判断
当访问站点的根url,如果该域名直接指向S3,且S3的桶ACL设置为public READ
,访问根url就会出现S3桶文件对象的遍历。
可以在<Name>
标签中看到桶的名称。
5、将FQDN(fully qualified domain name,完全限定域名)当作桶的名称
关于FQDN,这里直接照搬GPT给的解释:
其实简单理解就是网站的域名...
回到正题。
如果你发现一个域名或子域名指向了一个S3存储桶,但无法直接获取存储桶的名称,可以尝试使用该域名或子域名本身作为存储桶的名称进行尝试。这是因为一个常见的配置是将存储桶命名为与指向它的域名相同,以简化存储桶的访问和管理。
具体示例,文章并没有给出。下面是GPT给的示例:
如果这种方式行不通,还可以通过以下方式找到存在的桶名称:
- 通过google,或者网络空间搜索引擎(如Zoomeye,FOFA等)查看域名的搜索记录,看下是否有暴露桶名称。
- 访问文件对象时,查看响应头是否有包含桶名等信息的元数据。
- 暴破. 不要一股脑跑字典,而是根据站点用途、或者保存的文件的类型或文件用途来猜测。比如一个桶在
media.acme.edu
域上保存了ACME的音频文件,可以尝试这些作为桶名:media.acme.edu
、acme-edu-media
、acme-audio
、acme-media
等。
如果桶名不存在,会返回NoSuchBucket
;存在则返回AccessDenied
,或者列出桶内的文件对象。
也许还会碰到返回AllAccessDisabled
,这代表桶这些桶已经完全失效了。
AWS S3桶的内置权限组
ID/emailAddress
如果设置为ID/emailAddress
,表示允许单个用户(用户ID或用户的邮箱账户)对存储桶具有特定访问权限。
AuthenticatedUsers
这一项是最容易被误解的内置权限组,如果误用,则会导致安全风险。
如果设置为AuthenticatedUsers
,并不是表示需要特权用户才可以访问!而是所有具有S3凭证的用户都可以访问!
AllUsers
如果设置为AllUsers
,不需要身份认证,任何人都具备S3桶的读或写权限(发出PUT请求来修改或发起GET请求来下载对象,具体取决于配置的策略)
策略权限/ACP(Access Control Policies,访问控制策略)
这是S3提供的更细粒度的访问控制机制。它可以针对存储桶,或桶内的文件对象上设置策略权限。在文件对象上设置的策略权限,优先级比存储桶的策略权限更高。如:
READ
读权限,顾名思义。
READ_ACP
此权限允许读取存储桶或文件对象的访问控制列表(ACL)。如果启用该权限,则可以识别出易受攻击的资产。
WRITE
写权限,顾名思义。要注意的是,如果在桶设置了可写权限,那么桶里文件对象必须服从该桶的设置,换言之,即便桶内的文件对象没有设置写权限,桶内的文件依旧是可写的。
WRITE_ACP
此权限允许修改存储桶或文件对象的访问控制列表(ACL)。
如果启用该权限,是非常糟糕的选择,因为它会导致很高的安全风险。攻击者可能不能读取存储桶中的文件对象,但可以完全修改文件对象的访问控制。
攻击场景
场景
- Bucket used on a domain owned by the company
- Assets from bucket used by the company
公司使用的资产可能并不总是由该公司拥有。
文章给了两个自动化工具,实现在一个网站中自动化收集存储桶的信息:
(1) Second Orde,但Second Order 仅检查 HTTP 响应中引用的资产,不会检查动态加载的文件。
(2) 另一个使作者写的一个利用 chrome headless实现的示例:
首先在端口9222上启动headless版本:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222 --disable-gpu --headless
然后使用一个小脚本,如下:(其中 context.js 来自HAR-capturer-project,因为它可以正确关闭选项卡)
const CDP = require('chrome-remote-interface');
const URL = require('url').URL;
const Context = require('./context');
async function log_requests(orig_url) {
const context = new Context({});
process.on('SIGTERM', function () {
context.destroy();
});
try {
const client = await context.create();
const {Network, Page} = client;
const ourl = new URL('http://' + orig_url);
const ohost = ourl.host;
Network.requestWillBeSent((params) => {
if (params.request.url.match('^data:')) {
return;
}
const url = new URL(params.request.url);
console.log(ohost + ':' + url.host + ':' + params.request.url);
});
await Promise.all([Network.enable(), Page.enable()]);
await Page.navigate({url: 'http://' + orig_url});
await Page.loadEventFired();
await Page.navigate({url: 'https://' + orig_url});
await Page.loadEventFired();
} finally {
await context.destroy();
}
}
const url = process.argv.slice(2)[0];
log_requests(url);
这为我们提供页面上的所有资产,然后我们可以使用它们来确定其是否由 S3 提供:
- Bucket randomly found, indications it's owned by the company
需要有明确的证据证明该桶确实属于该公司所有。尝试查找指向此存储桶的公司的引用,例如其网站上的引用、CI 日志或开源代码。
要测试的点:
- 桶的READ权限是否开启
具体:文件对象遍历
- 桶的READ_ACP是否开启
可以直接识别桶的脆弱点。如果是在 AllUsers
或 AuthenticatedUsers
权限组的基础上,还设置了WRITE_ACP
权限,就可以完全控制这个存储桶了。
- 桶的WRITE权限是否开启
可以在不破坏桶的文件的情况下测试: 可以在PUT
写入接口,加上Content-MD5
请求头,告诉AWS校验上传文件的MD5。
因为S3会先校验写权限,再校验MD5。 - 文件对象的
READ_ACP
是否开启 - 文件对象的
WRITE_ACP
是否开启
潜在的漏洞
- XSS
如果存储桶启用了WRITE
,就可以写入恶意js、svg、html这样的文件,在网站引入的时候导致XSS. - DoS
如果存储桶启用了WRITE_ACP
,可以修改访问控制为私有,使存储桶的无法被公开访问。 - 信息泄露
如果存储桶启用了READ
,则允许文件对象遍历。 - RCE
如果存储桶可写,则可篡改已有的可执行文件。不过这也取决于该可执行文件是否存在被执行的环境。