JTopCMS审计之目录穿越漏洞

admin 2023年12月10日17:56:12评论27 views字数 9128阅读30分25秒阅读模式

一、环境部署

1.1、官方网站

https://www.jtopcms.com/

1.2、下载地址

https://gitee.com/mjtop/JTopCMSV3/releases/tag/JTopCMSV3.0.2-OP

1.3、所需环境

名称

版本

JTopCMS

V3版本(开源版本)

Java版本

JDK1.7+即可

mysql

5.5.29

Tomcat

Tomcat7+即可

1.4、修改配置

在JTopCMSV3-JTopCMSV3.0.2-OPWebContentWEB-INFconfigcs.properties配置文件中修改端口信息(与配置的Tomcat保持一致)与数据库连接信息    

JTopCMS审计之目录穿越漏洞

1.5、启动项目

启动项目之后,来到后端管理登录页面:

http://127.0.0.1:7089/login_page

JTopCMS审计之目录穿越漏洞

登录账号密码为 admin/jtopcms    

二、代码审计

今天我们只针对这个应用的目录穿越漏洞进行审计,而目录穿越漏洞一般伴随文件上传/下载/删除等功能,所以我们先查找一下该程序是否存在文件上传/下载/删除功能

那审计一个项目时如何快速定义到系统的下载功能呢?

1、项目中的使用文档用户手册

2、部署环境后通过抓取下载数据包确定接口名后全局搜索接口名

3、使用经典关键字download等进行全局搜索即可

            

这里通过关键字检索,我们发现了两个疑似文件下载的接口

JTopCMS审计之目录穿越漏洞

下面我们分别来对这两个下载功能的接口进行追踪与漏洞测试

                

1、/downloadback.do

(1)功能点定位

首先定位到映射代码位于BackupSystemController类中,如下

JTopCMS审计之目录穿越漏洞

再定位到请求该接口的jsp文件中,如下

JTopCMS审计之目录穿越漏洞

通过方法名—downloadBak和该jsp文件名ManageBackup.jsp猜测该功能是一个备份文件管理功能,并且在备份下载功能中调用了该接口

所以来到后台寻找该功能点,成功定位到下载功能点,并且确实存在备份文件下载功能,功能点位于    

                

后台->系统配置->系统备份管理

                

JTopCMS审计之目录穿越漏洞

抓个包再次验证

JTopCMS审计之目录穿越漏洞

进一步确认了该功能点调用了/downloadback.do接口

(2)源代码审计

1、Controller层    

根据请求包来看,target,pw,downFileInfo,这三个参数都有可能与文件下载功能有关,

我们给下载备份这个功能点下一个断点,再调试项目,看看程序的执行流程

JTopCMS审计之目录穿越漏洞

简化后的流程代码如下

@RequestMapping( value = "/downloadBak.do", method = { RequestMethod.POST } )                    public String downloadBak( HttpServletRequest request, HttpServletResponse response )                        throws UnsupportedEncodingException                    {                        // 将请求传入的参数存储在Map对象                        Map params = ServletUtil.getRequestInfo( request );                               // 通过session判断用户身份是否合法                        .....                               // 从请求中获取target参数的值,赋值为targetbak                        String targetBak = ( String ) params.get( "target" );                                      // 获取系统的真实路径并赋值给base变量                         String base = SystemConfiguration.getInstance().getSystemConfig().getSystemRealPath();                                      // 构建一个如下的字符串,并赋值testbakroot                         String testBakRoot = base + "__sys_bak__" + File.separator + targetBak;                               // 检查了名为testBakRoot的路径是否指向的是一个文件,并为其创建File对象                        ......                               // 开始下载File对应的文件                        ......                                      // 关闭流                        ......                        // 抛出异常                        ......

分析上面的代码,可以看我们传入的target会被直接拼接到如下路径,在这段代码中并没有防范

{base}/_sys_bak_/{target}

base在我的项目中的值如下

E:/WorkSpace/Javawork/JTopCMSV3-JTopCMSV3.0.2-OP/target/ROOT

即拼接后的备份完整路径为(假设备份文件名为bak.zip,即target为bak.zip)

E:/WorkSpace/Javawork/JTopCMSV3-JTopCMSV3.0.2-OP/target/ROOT/_sys_bak_/bak.zip

于是我在

E:WorkSpaceJavaworkJTopCMSV3-JTopCMSV3.0.2-OPtargetROOT

目录下新建了一个a.txt,再请求/downloadbak接口,传入的target的值改为..a.txt,尝试拼接为如下的接口进行读取a.txt,从而验证任意文件下载漏洞

E:WorkSpaceJavaworkJTopCMSV3-JTopCMSV3.0.2-OPtargetROOT_sys_bak_..a.txt

JTopCMS审计之目录穿越漏洞

结果如下    

JTopCMS审计之目录穿越漏洞

可以看到被拦截了,并且给出提示:包含非法字符

控制台打印如下

JTopCMS审计之目录穿越漏洞

这里我们思考一下,刚才我们Controller层代码中并没有检测target的合法性,但现在我们传入恶意的target又被拦截了,说明了什么?说明这个downloadbak请求到达Controller类代码之前被拦截并执行了检测,第一时间就想到了去看看Spring的配置文件中是否配置了拦截器

2、interceptor层

果不其然,在其中找到了这样一段Spring MVC的拦截器配置代码,如下

<mvc:interceptors>                
               // 第一个拦截器                
                      <mvc:interceptor>                
                                                
                                 <mvc:mapping path="/**" />                
                                 <bean class="cn.com.mjsoft.cms.common.interceptor.SpringMVCFlowExeTokenAndTraceInterceptor" />                
                      mvc:interceptor>                  
                 
               // 第二个拦截器                  
                      <mvc:interceptor>                  
                                 <mvc:mapping path="/survey/clientVote.do" />                  
                                 <mvc:mapping path="/guestbook/clientAddGb.do" />                  
                                 <mvc:mapping path="/content/clientAddContent.do" />                  
                                 <mvc:mapping path="/content/clientEditContent.do" />                  
                                 <mvc:mapping path="/content/deleteContentToTrash.do" />                  
                                 <mvc:mapping path="/clientAddComment/clientAddComment.do" />                  
                                 <mvc:mapping path="/deleteComment/deleteComment.do" />                  
                                 <mvc:mapping path="/resources/clientDf.do" />                  
                                 <mvc:mapping path="/member/*.do" />                  
                                 <bean class="cn.com.mjsoft.cms.member.interceptor.MemberActScoreInterceptor" />                  
                      mvc:interceptor>                    
                   
               // 第三个拦截器                    
                      <mvc:interceptor>                    
                                 <mvc:mapping path="/member/*.do" />                    
                                 <bean class="cn.com.mjsoft.cms.member.interceptor.MemberSendMessageInterceptor" />                    
                      mvc:interceptor>                      
                     
               // 第四个拦截器                      
                      <mvc:interceptor>                      
                                 <mvc:mapping path="/content/addContent.do" />                      
                                 <mvc:mapping path="/content/editContent.do" />                      
                                 <mvc:mapping path="/content/clientAddContent.do" />                      
                                 <mvc:mapping path="/content/clientEditContent.do" />                      
                                 <bean class="cn.com.mjsoft.cms.content.interceptor.DeleteTempFileWhenUploadErrorInterceptor" />                      
                      mvc:interceptor>                        
                       
mvc:interceptors>                  

可以看到配置了四个拦截器,这里能够匹配到/resources/donloadbak.do请求的只有第一个拦截器

这里提一下以下两种情况

1、如果有多个拦截器都能匹配,则拦截顺序默认按照配置拦截器的顺序进行

2、Spring MVC中,可以通过在配置文件中使用mvc:interceptor-ref标签来引用并配置拦截器,并使用order属性来指定它们的顺序,这样的话拦截顺序则变为指定顺序    

回到正题,经过上面的分析,我们的/resources/downloadbak.do请求会先被第一个拦截器处理,所以我们跟过去看看第一个拦截器类(即SpringMVCFlowExeTokenAndTraceInterceptor类)的内容,如下

JTopCMS审计之目录穿越漏洞

可以看到这个类实现了Spring的HandlerInterceptor接口,用于拦截请求并执行一些前置和后置的处理,这里我们要分析它对拦截的请求的检测逻辑,所以主要分析前置处理代码,即preHandle方法    

JTopCMS审计之目录穿越漏洞

简单看了下preHandle方法,只是进行了权限校验,也没有对参数特殊字符的处理

Controller类中没有处理,Controller类之前的拦截器也没有处理,那还有什么能够在Controller类和拦截器之前对请求进行处理呢?答案就是过滤器(Filter)

3、Filter层

过滤器的配置可以到Web应用的配置文件web.xml文件中查看,如下    

JTopCMS审计之目录穿越漏洞

可以看到这里配置了两个过滤器,第一个拦截的是所有请求,第二个拦截的是.do结尾的,我们刚才被拦截的请求是

/sources/downloadbak

所以会被第一个Filter拦截,我们定位到到第一个Filter对应的Filter实现类中,即InterceptFilter类

JTopCMS审计之目录穿越漏洞    

可以看到InterceptFilte类虽然名字叫InterceptFilte(拦截器),但其实实现的是Filter接口的一个自定义过滤器,因为刚才控制台打印了如下两句话

[2023-12-09 15:54:27,756] FATAL  - danger char->..                
[2023-12-09 15:54:27,757] FATAL  - IP->172.24.86.134,非法动作->target=../a.txt&pw=,URL->http://172.24.86.134:8090/resources/downloadBak.do

说明是我们传入的target=../a.txt中带了".."被识别为危险字符了,所以先搜索一下".."字符,如下

JTopCMS审计之目录穿越漏洞

在静态代码块中成功找到定义,而且这个$6的内容看着也像黑名单字符,所以我们先试着追踪一下$6的流转

可以看到288行调用当前类的$1方法时,将$6作为参数传入了方法

JTopCMS审计之目录穿越漏洞

我们分析一下_$1方法,如下    

JTopCMS审计之目录穿越漏洞

经过分析,发现确实就是它导致了我们的..被拦截,分析流程如下

private boolean _$1(String var1, String[] var2, String var3, boolean var4) {                
    _$13.debug("{SYSTEM Adjudgement} 将验证参数:" + var1);                
    // 判断var1是否为空,为空就退出                
    ......                
    //不为空就继续执行                
    if (var1.startsWith("http://")) {                
                      //如果URL以 "http://..." 开头,检查它是否与已知站点的URL匹配,或者是否包含特定危险字符。                
                      //如果匹配或包含危险字符,记录错误日志,返回 false 表示不安全。                
     }                
     // 如果var4=true                
                // 就检查var1是否包含危险字符                
                // 包含的话就打印错误信息并退出返回false                
               
     // 如果var4=false                
                      // 就检查var1是否包含危险字符和“=”号                
                // 包含的话就打印错误信息并退出返回false                
               
    return true;                
                   
}

简单来说这段代码就是用于检测var1是否包含提前定义好的危险字    

JTopCMS审计之目录穿越漏洞

所以刚才我们的控制台上会出现 danger char->..这个信息

因为刚才的判断代码返回false,即我们的输入不安全,所以这里的288内的代码块会被执行

JTopCMS审计之目录穿越漏洞

导致var55被赋值为false,从而执行第299行代码,即调用$1方法,$1方法打印出本次导致错误的动作信息,如下

JTopCMS审计之目录穿越漏洞

这也就解释了我们控制台的第二句打印

IP->172.24.86.134,非法动作->target=../a.txt&pw=,URL->http://172.24.86.134:8090/resources/downloadBak.do    

所以这个/downloadbak.do下载接口是有特殊字符过滤的,我在这里试了很久,但还是没有办法绕过,即没有办法配合目录穿越 ../ 来读取任意位置的敏感文件,所以我个人认为这里暂时是没有漏洞的。

进入下一个下载功能点。

/downloadresfile.do

(1)功能点定位

首先定位到关键代码

JTopCMS审计之目录穿越漏洞

可以看到关键代码位于ManageSiteFileAndCheckController类中,再请求到定位该接口的jsp文件中,如下    

JTopCMS审计之目录穿越漏洞

成功定位到ManageTemplate.jsp文件中,并且还传入了一个entry参数

JTopCMS审计之目录穿越漏洞

根据文件名和文件信息确认该功能点为模板管理功能的下载功能,如下    

JTopCMS审计之目录穿越漏洞

(2)源代码审计

根据上一个下载的功能点,我们知道了请求参数的值中不能包含指定的危险字符,基于这个前提,我们再来审计一下这个下载的功能点

审计上一个下载点的时候,我们已经把过滤器拦截器分析过了,这里就不分析了,直接看Controller类的代码,如下

JTopCMS审计之目录穿越漏洞

1、先分析211-219行,从请求中获取mode、entry、downFileInfo参数的值,并且entry和downFileInfo两个参数还经过了SystemSafeCharUtil.decodeFromWeb方法处理

2、校验用户身份是否合法

3、根据日期随机生成一串字符,拼接.zip,形成一个类似下面这样的压缩文件名

*sys_template_temp*demo_2023_465461.zip    

JTopCMS审计之目录穿越漏洞

发现在267行拼接了压缩文件名,跟进getFullPathByManager方法来到ResourcesService.java类

JTopCMS审计之目录穿越漏洞

主要代码如上,简单来说该方法就是把zipName中的*替换为路径分隔符(""或者"/"),再过滤掉一些敏感字符,再与根路径进行拼接,最终得到类似如下的fullZipPath

E:Work

SpaceJavaworkJTopCMSV3-JTopCMSV3.0.2-OPtargetROOTdemosys_template_tempdemo_2023_465461.zip

\ 其中*sys_template_temp*demo_2023_465461.zip为传入的zipName(entry)

                

后面的代码简单的说就是基于根目录,压缩template(entry参数的值)文件夹下的block(downloadfileinfo参数的值)文件夹下的内容并复制到sys_template_temp目录下,同时供用户下载    

所以就从头梳理了一下,想起了最开始获取请求的entry、downFileInfo参数的值时,获取后调用了SystemSafeCharUtil.decodeFromWeb方法,所以我们追踪SystemSafeCharUtil.decodeFromWeb方法,如下

JTopCMS审计之目录穿越漏洞

可以看到该方法将传入的input参数进行了一次url解码后,再调用decodeDangerChar()方法,我们继续追踪该方法    

JTopCMS审计之目录穿越漏洞

JTopCMS审计之目录穿越漏洞

通过代码可以得知该方法主要用来完成替换危险字符的操作,如果路径中存在一些危险字符的话就会被该方法替换为指定字符,同时我观察到

*!4* 则会被替换成".."

*!11* 会被替换成""

这样说的话路径中存在 *!4**!11*的话就会变为"..",这样是不是就又可以向上遍历目录了?

于是我们来尝试读取一下电脑上的其他文件,例如

E:info.txt

经过我们上面的分析,得出下载的文件根目录    

E:WorkSpaceJavaworkJTopCMSV3-JTopCMSV3.0.2-OPtargetROOTdemo

根据传入的entrydownFileInfo

最后构成

E:WorkSpaceJavaworkJTopCMSV3-JTopCMSV3.0.2-OPtargetROOTdemo{entry}{downFileInfo}

所以我们把downFileInfo修改为要读取的info.txt,entry也利用多个*!4**!11*穿越到E盘的根目录,

尝试形成如下的文件链接

E:WorkSpaceJavaworkJTopCMSV3-JTopCMSV3.0.2-OPtargetROOTdemotemplate................info.txt

从而读取到

JTopCMS审计之目录穿越漏洞

成功读取到目标文件    

JTopCMS审计之目录穿越漏洞

三、总结

到这里,JTopCMS的下载功能的目录穿越漏洞就审计完毕,看到Power7089师傅在讲解目录漏洞时讲了这个CMS的下载功能存在任意文件下载漏,就想着试一试自己能不能找到,在第一个下载点的审计花了很久,因为一直没找到“../”是怎么被拦截下来的,并且找到拦截代码之后尝试绕过也试了很久,还是没绕过去,审计第二个就比较容易了,直接在Controller类中就跟踪到了问题代码,审计完之后再和Power7089师傅的审计过程比对一下,发现确实是恶意字符替换导致的问题,这里贴一下Power7089师傅审计该漏洞的文章链接

https://power7089.github.io/2022/11/29/JavaWeb代码审计实战之配合JtopCMS实战讲解目录穿越漏洞/

最后,真的想吐槽一下,开发人员添加这个替换恶意字符的操作的意义是什么?明明Filter中都已经做了很好的拦截操作了,比如第一个下载的功能点,全靠Filter拦截,第二个下载点非要多此一举,替换后的字符和Filter中拦截的字符有些都是重复的,替换的意义是什么,真有点没搞明白。    

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月10日17:56:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JTopCMS审计之目录穿越漏洞https://cn-sec.com/archives/2284457.html

发表评论

匿名网友 填写信息