3月2日,CNVD披露了编号为CNVD-2023674205的nacos认证绕过漏洞
在nacos官方github项目(https://github.com/alibaba/nacos)发布2.2.0.1版本对应修复版本,移除了默认鉴权插件中依赖的`nacos.core.auth.plugin.nacos.token.secret.key`默认值
下载2.2.0版本nacos搭建环境,下载链接:https://github.com/alibaba/nacos/releases/download/2.2.0/nacos-server-2.2.0.tar.gz
启动也很简单,先安装好java环境(如何安装java自行搜索 (~ ̄▽ ̄)~),进入bin目录下,执行./startsh -m standalone即可
# 解压缩
unzip nacos-server-1.0.0.zip
# 进入bin目录
cd nacos/bin
# 运行nacos 注意添加 -m standalone
sh startup.sh -m standalone
# 关闭nacos
sh shutdown.sh
根据官方的修复公告,查看配置文件 conf/application.properties,发现nacos.core.auth.plugin.nacos.token.secret.key
是有默认值的
而nacos使用jwt构造认证token,使用HS256算法,把配置文件中nacos.core.auth.plugin.nacos.token.secret.key
的默认值当作私钥生成Signature,将subject(用户名)和exp戳写到jwt token里
查看源码:nacos-2.2.0\plugin-default-impl\src\main\java\com\alibaba\nacos\plugin\auth\impl\JwtTokenManager.java
其中secretKey即配置文件中nacos.core.auth.plugin.nacos.token.secret.key
的值,从JwtTokenManager类的processProperties()函数中可以看到
在jwt token校验的时候,校验了token签名有效性和是否过期(nacos-2.2.0\plugin-default-impl\src\main\java\com\alibaba\nacos\plugin\auth\impl\NacosAuthManager.java)
现在知道了jwt的生成和校验逻辑,以及jwt的默认私钥,就可以伪造jwt token绕过认证nacos认证逻辑,构造一个超长有效期的token,就可以使用poc扫描啦
下面进行验证,以获取配置信息接口为例,首先是没有jwt token访问接口,返回禁止访问
带上Authorization再访问,jwt token使用刚才构造的,成功返回
ps:在老版本中(1.1.4版本及以下),如果nacos没有配置信息,则body中没有数据,仅返回200,所以poc不能以响应包的body为标志进行扫描,若以status_code为标志,则误报会很多
可以删除search参数值,以响应包的body为标志( •̀ ω •́ )✧
ps:0.x.x的远古版本没有登录逻辑。。访问就直接进入系统了
1.2.0版本及以上的nacos,修改配置文件中的nacos.core.auth.plugin.nacos.token.secret.key
即可
1.1.4版本及以下的nacos,由于私钥写死到了代码里,用户无法自行配置,只能升级nacos到最新版
一开始用登录接口来检测jwt默认私钥漏洞,在1.2.0及以上版本中,使用构造的jwt token访问,发现即使不输入账密,也显示登录成功,接口地址为/nacos/v1/auth/users/login(默认配置启动),响应包中校验200状态码和accessToken字段即可
但在老版本(1.1.4及以下版本)中,接口地址为/nacos/v1/auth/login(默认配置启动),跟新版本的不一样,并且使用了伪造的jwt token也不成功,造成了漏报(っ °Д °;)っ
勾起了好奇心,下载nacos源码一探究竟:
首先是老版本(1.1.4及以前)的源码,登录逻辑为直接比对账密,比对成功即登录成功
而在新版本(1.2.0版本-最新)中,增加了一个header中token的认证逻辑:如果配置文件中的nacos.core.auth.system.type为nacos或者ldap,且请求包含Authorization的header并校验通过,则优先使用请求中的token,可以不用校验账密
配置文件(nacos.core.auth.system.type默认为nacos):
登录部分的源码(nacos-2.2.0\plugin-default-impl\src\main\java\com\alibaba\nacos\plugin\auth\impl\controller\UserController.java):
校验通过后,响应包会直接使用请求包中的jwt token
跟进authManager.login(request)函数,到达nacos-2.2.0\plugin-default-impl\src\main\java\com\alibaba\nacos\plugin\auth\impl\NacosAuthManager.java里面
在resolveToken和validate0函数里校验
resolveToken函数用于提取Authorization的值,若以Bearer 为开头,则取第7个字符以后的字符串返回
validate0用于校验jwt token,使用配置的nacos.core.auth.default.token.secret.key
响应包中返回登录成功,响应包中包含请求header中的token,而老版本仅校验账密导致返回登陆失败
所以新版本中使用登录接口验证默认私钥漏洞不适用于老版本,构造一个全版本通杀的poc还是不容易的o(TヘTo),还是加上获取配置信息接口一块扫吧。。
nacos历史上曾经爆出过使用特殊构造的User-Agent造成未授权接口访问的漏洞,在翻官方文档(https://nacos.io/zh-cn/docs/auth.html)中发现这么一段话:
由此可见,使用特殊构造的User-Agent是官方用于服务端之间的可信通信,未考虑到暴露公网的情况,在nacos 1.2-1.4.0版本期间存在这个安全问题,在1.4.1及以上版本中,nacos默认配置关闭了这个特性,用户可以手动开启,开启后需要配置自定义的key value对
在写poc时,我的expression是这样写的,校验响应包头中是否有Content-Type字段,并判断是否为application/json
看着莫得问题,结果poc扫描器显示poc加载失败
看了看官方文档,也没发现啥问题,文档里也这么用的
调试了半天,发现前面一定要有response.status的判断才能加载成功。。
但是响应包的状态码本来就不确定,可能为200 400 500,所以就没加状态码的判断,结果就卡在这里了。。(不晓得为啥一定要先判断response.status才能检查headers)
被逼无奈,只能写了个response.status != 1来意思一下了(*  ̄︿ ̄)