1 漏洞exp
nuclei的yaml文件
1 | id: TianRui-LvDun-UploadWxFile-RCE |
数据包
1 | POST /trwfe/login.jsp/../config/uploadWxFile.do |
2.漏洞分析
这个漏洞有两个点需要分析,一个是上传功能,上传的文件没有检查,文件名也没有过滤,另一个是鉴权,从exp可以看到出现了../目录穿越。

看一眼源码,mvc
然后看一眼过滤器,拦截器和aop中是否有过滤代码
filter过滤器中未发现。

interceptor拦截器中未发现,拦截器中实现的功能是防止token重复提交

AOP中未发现,这段代码作用只是从请求中获取用户信息

上传
根据exp路由/uploadWxFile.do定位到controller中,找到
return this.configService.uploadWxFileToRoot(file, request, response);

随后定位到/trwfe/service/impl/ConfigServiceImpl.java


这里把相关代码贴出来
1 | public boolean uploadWxFileToRoot( MultipartFile file, HttpServletRequest request, HttpServletResponse response) { |
代码很好懂
入参只有一个file,然后拼接一个临时路径tempPath为./tomcat/webapps/ROOT/,随后创建该文件夹。其中 System.getProperty("catalina.home")为tomcat安装目录的绝对路径
然后os = new FileOutputStream(new File(tempPath + file.getOriginalFilename()));这一行tempPath + file.getOriginalFilename()中,就把文件名拼接上路径了,没有作任何过滤,第二行`IOUtils.copy(file.getInputStream(), os);``就是保存文件的过程了。
上传这一个功能点就没什么好说的了
鉴权
从exp中可以看到../可以知道是有绕过操作的,从代码中来看看是怎么回事。
在controller中没有发现鉴权注解,无论是/config还是/uploadWxFile.do

那就去看过滤器,拦截器和aop
拦截器
拦截器中只有一个TokenInterceptor,看代码是获取token用的,没提到过滤的事情

aop
aop中这段代码作用只是从请求中获取用户信息

过滤器
过滤器一共有5个,RequestWrapper、ResponseWrapper、RestFilter、SecurityFilter和SessionFilter。前三个看了一眼,和鉴权无关,RequestWrapper主要是获取请求体长度,ResponseWrapper中也没什么东西,至于RestFilter是记录接口日志相关的东西,看上去也和鉴权无关。所以只需要看SecurityFilter和SessionFilter。
SecurityFilter中只有一个判断url是否需要认证的,关键函数isNoNeedValidate,检查url是否不需要校验。函数还在SessionFilter中,这下只需要看SessionFilter了

SessionFilter,首先是和前面SecurityFilter一样检查url,判断是否不需要校验,
如果是,直接结束filter,
如果否,检查第三方系统开关是否开启且类型为2,同时检查是否是禁用的url,这里用到的isForbid函数。如果是,返回500。
如果否,继续进行检查通过获取x-requested-with请求头,尝试获取cookie中的lang字段,如果为空,则返回time out,

这里展示集中不同情况下的服务端返回情况
非白名单-401

白名单-根据业务来


非白名单- x-requested-with=XMLHttpRequest,lang 不为空,返回401,虽然sessionfilter中,同样是和前面一样,返回到了chain.doFilter(servletRequest, servletResponse);中,但是因为还有一个SecurityFilter,在这里还会校验url,所以还是会返回401。
在web.xml中可以看到springSecurityFilterChain接管了所有*.do的接口

非白名单- x-requested-with=XMLHttpRequest,lang 为空,返回sessionstatus: timeout

回到最后,要看鉴权只需要看isNoNeedValidate函数,相关代码全部贴上来
1 | public static boolean isNoNeedValidate(String url, HttpServletRequest request) { |
代码简单易懂,传入一个url,判断url是否是以paths列表中的元素开头,如果是,返回true,如果不是,继续到isDdWxDownLoad函数中判断,在这个函数中,首先看DING_QYWX_DOWNLOAD_FILE参数是否为true,这个参数是一个开关,默认打开。然后就是和前面一样的判断url是否以paths中的元素开头。


因为只是一个startsWith函数进行鉴权判断,所以可以轻易通过../../进行绕过,随便选一个白名单元素进行拼接即可。
修复和一点外行的猜测
观察paths,其实不难猜测开发为什么会这样写,因为其中除了一些正常的*.do、*.jsp路径,还有四个"/service/","/ding/", "/wx/", "/fanwei/"这样的目录,一个偷懒就直接startsWith了,
所以这个漏洞要修复的话,首先要修复这个权限绕过,可以在isNoNeedValidate函数第一行加上
1 | public static boolean checkTraversal(String content) { |

然后是上传逻辑中的uploadWxFileToRoot函数,加上文件后缀的校验