用友NC Cloud ncchr登陆校验中存在安全特性绕过漏洞,由于登陆调用Loginfilter.class类针对request请求头中accesstokenNCC解密校验场景缺失,因此存在模拟访问时,特定accesstokenNCC序列串校验永不失效。
根据安全通报中的漏洞描述,对于ncchr的路由鉴权存在漏洞。
通过安装包安装完成站点,然后将服务端的代码打包使用idea打开。可以先通过web.xml看一下路由信息。系统有用spring框架,路由还是比较好分析。
可以看到在路径中访问ncchr由什么应用服务处理,这里可以再去WEN-INF下的lib里找相应的jar包。然后反编译工具或者使用idea,lib导入jar查看源码。
这里继续搜索发现ncchr路由的过滤器。
其中应该就是相应鉴权代码,进入相应文件查看。
可以看见LoginFilter继承至Spring框架中的OncePerRequestFilter,通过OncePerRequestFilter这个类我们知道LoginFilter对于同一个请求只被调用一次,同时定义了一些静态常量字符串用于识别鉴权请求中的特定参数和后续的操作的附加信息
在继续分析之前,我们需要先了解过滤器的生命周期。过滤器的生命周期通常包括以下阶段。
其中validAccessToken方法如上,主要操作为获取HTTP 请求中的accessTokenNcc(在上面中accessTokenNcc被定义成私有的静态字符串ACCESSTKEN_NCC),然后进行判断为空直接返回false。获取InvocationInfoProxy(用来持有用户请求信息的代理类)的实例,然后进行JWT解密。就是从token中取出值设置到实例中,包括用户ID、组ID、数据源、设备ID、用户代码以及语言代码。跟进JwtTokenUtil工具类查看具体校验流程,
根据以上可得到,JWT校验流程,为先获取JwtTokenUtil的单例实例,然后通过实例中的getPrivateClaimFromToken方法来处理传入的token,其中调用了getClaimFromToken方法来获取token中的私有声明,具体代码如下。
主要逻辑为通过parser()方法创建一个JwtParser实例(用来验证和解析JWT,提取其中的信息并进行相应的处理。),然后通过从jwtProperties中获取的密钥解析传入的token,获取声明。其中跟进jwtProperties代码可以发现,jwt的默认密钥硬编码到了代码中,所以我们可以自己生成jwt即可绕过鉴权。
后续流程为有错进行日志记录,没有就执行LoginHelper.ensureSecurity();
从代码可知ensureSecurity()是设置一个安全令牌,这样我们就可以访问ncchr的路由。
绕过前台的校验,我们就可以查看后台的是否存在漏洞。
在/ncchr/pm/fb/attachment.java中存在文件上传功能
@RequestMapping定义了一个请求映射,将POST到/uploadChunk路径的请求路径到uploadChunk方法,查看代码,uploadChunk方法实际是处理文件分片上传请求的。接收参数为(HttpServletRequest request, MultipartFile file, Integer chunk, Integer chunks, String fileGuid, String operation)
其中Integer对象分别为分片序号和总分片数,这个不重要。重要是fileGuid为string类型,跟进后面可以发现fileGuid为文件名,且在当前控制层未发现对文件名的处理。
Object obj = this.attachmentService.uploadAttachment(file, fileGuid, chunk, operation, isMultipart, chunk.intValue() == chunks.intValue() - 1);
具体上传代码在attachmentService接口的实现类中,进入相应代码查看
首先构造了相应的临时文件路径,由SpringHelper.getRealPath()(用于获取应用程序运行的环境的实际路径),File.separator(提供了系统相关的默认名称分隔符),AppContext.getContext().getScope()(当前的作用域) ,然后和输入的文件名拼接组成了存储上传文件的路径。
然后就是通过Spring框架中处理上传文件的方法上传文件,getOriginalFilename就是获取原始文件名。后续的代码就是处理分段上传和是否上传至OSS相关的代码。现在我们已经掌握了上传任意文件至任意目录的条件。文件类型可控,且可以通过../穿越目录。便可以上传jsp文件至可执行目录。
通过分析代码发现,应用没有使用ORM持久层框架,所有的查询都是使用拼接字符串的方式来构造查询条件。根据关键字搜索,可以找到,如下几个模糊查询的实现类中的方法。
以上代码执行的操作都为,定义默认的查询条件,对传入参数进行判断是否为空,不为孔则将其作为查询条件加入到原始条件中,然后调用 queryCommonSimpleStaffPage 方法进行实际的查询,然后返回page对象。queryCommonSimpleStaffPage 方法代码如下。
主要就是做获取分页的排序信息,处理排序信息等的操作,然后调用 queryCommonSimpleStaffList
方法查询符合条件的员工信息列,进入相应方法查看。
首先判断查询条件为空,为空设定为默认条件"1=1",然后在查询条件中添加组织信息,限制查询结果为当前组织,再使用 IPsndocQryService 进行查询。此时我们已经找到存在SQL注入的实现方法,只需要寻找到哪些接口掉用了这些方法,且对参数未进行过滤。
这里我们找到一个queryStaffByName接口。
其中对name参数的查询调用了StaffHelper.queryStaffByName方法,其中queryStaffByName方法,又调用了querySimpleStaffPageByName,也就是上文中存在SQL注入的实现方法。
并且对输入name未有任何过滤和检测操作,所以这里我们构造符合条件的数据包就可以进行漏洞检测。构建延时注入命令检测。
使用sqlmap