STS全称安全凭证服务(Security Token Service),在云服务中最常用到的临时身份管控方案。无论是在前端文件上传、亦或是子账号权限分配,他通过主(子)账户,去调用权限下发的API,在API参数中,配置相应的权限字符串(Policy),服务端则会解析用户上传的权限字符串,给出相应的的临时账户,一般由accessKey、accessKeySecret、STS Token组成。下面列举下国内常见的STS文档:
STS身份注入攻击
身份注入漏洞一般发生在云租户允许用户去自定义权限字符串(Policy)中的部分元素时存在,例如在对象存储上传时,服务端使用STS进行权限控制,但是又允许用户输入上传的后缀、目录或大小时,会存在问题。这里我们以腾讯云为例(腾讯云STS已经在去年末,通过API服务侧限制Policy进行止血,理论不再会存在此类型漏洞)大概理一下此类漏洞的原理以及利用方式。
在文档中,我们可以看到,Policy的生成,依赖于 CAM 策略语法。
所谓的CAM策略语法,实际上就是一个由version、statement组成的json字符串,其中statement是我们需要给STS临时身份授权的具体策略列表,由核心元素包括委托人(principal)、操作(action)、资源(resource)、生效条件(condition)以及效力(effect)组成的json列表组合而成。下面是一个示例:
{"version": "2.0","statement": [{"principal": {"qcs": ["qcs::cam::uid/1238423:uin/3232523"]},"effect": "allow","action": ["cos:PutObject","cos:GetObject","cos:HeadObject","cos:OptionsObject","cos:ListParts","cos:GetObjectTagging"],"resource": ["qcs::cos:ap-beijing:uid/1238423:bucketA-1238423/","qcs::cos:ap-guangzhou:uid/1238423:bucketB-1238423/object2"],"condition": {"ip_equal": {"qcs:ip": "10.121.2.10/24"}}},{"principal": {"qcs": ["qcs::cam::uid/1238423:uin/3232523"]},"effect": "allow","action": "cmqqueue:SendMessage","resource": ""}]}
看完上面的示例,这里给一个很简单的案例,我们在开发上传API的时候,如果想要用户只能上传目录(dir)为userid,后缀(ext)为.txt的文件,应该如何进行去编写业务逻辑?
一般来说,业务会考虑从session中取得userid,后缀或是文件名,则可能从用户输入的参数中获取,再接入到我们的statement中,这样我们的策略就会变成(一般在后端调用不会存在换行符,这里为了美化展示,加上了换行符):
{"version": "2.0","statement": [{"principal": {"qcs": ["qcs::cam::uid/1238423:uin/3232523"]},"effect": "allow","action": ["cos:PutObject"],"resource": ["qcs::cos:ap-beijing:uid/1238423:bucketA-1238423/{dir}/{filename}.{ext}"]}]}
到这里,其实此类漏洞就已经很明显了,当我们的ext是用户可控的话,我们就可以构造出,类似于:
ext=txt"]},{"effect": "allow","action":[""],"resource":["qcs::cos:","qcs::cvm:*最终,发送到API的statement就成了我们修改过的恶意的,可以接管cos、以及cvm的statement,美化后如下:
{"version": "2.0","statement": [{"principal": {"qcs": ["qcs::cam::uid/1238423:uin/3232523"]},"effect": "allow","action": ["cos:PutObject"],"resource": ["qcs::cos:ap-beijing:uid/1238423:bucketA-1238423/{dir}/{filename}.txt"]}, {"effect": "allow","action":["*"],"resource":["qcs::cos:*","qcs::cvm:*"]}]}
在这个payload中,我们构造了两个statement,第一个为按照业务预期的,控制了后缀以后的statement,第二个则是由我们注入后,可以直接控制用户CVM、与COS所有权限的statement。当然,如果云服务的API server后端是使用的是一些特性比较多的JSON解析方案,payload还可以有更多变化,例如
ext="]}]}//ext=","qcs::cvm:"],"effect": "allow","action": ["","
具体如何拓展可参考各家使用JSON解析方案的支持语法,以及各家自身的具体实现。
此类漏洞修复方案,一般分为三种,其中两种需要深入每一处业务使用,最后一种只可以整体去避开此类风险。
类似SQL注入、SLS注入一样,在每次调用API之前,对用户输入的内容进行过滤与校验。
使用官方给出的SDK去调用,官方SKD一般会通过JSONObject类的方案去生成请求参数,会很大程度上避免这个问题,但是如果API Server有比较离谱的实现,就是另外一回事了。
使用权限限制较多的子账户去下发STS身份,这样可以避免注入后获取List、FullAccess等敏感权限。