x318846679
- 关注

因为漏洞未公开,就不直接放出payload了,已标注存在问题的点位,请自行分析和构造payload,这个漏洞理解还是不难的
<%@ page language="java" contentType="application/json" pageEncoding="UTF-8"%>
<%@page import="net.sf.json.*"%>
<%@ page import="java.util.*" %>
<%@ page import="weaver.general.*" %>
<%@ page import="weaver.file.*" %>
<%@ page import="weaver.hrm.*" %>
<%@ page import="weaver.mobile.rong.*" %>
<jsp:useBean id="ps" class="weaver.mobile.plugin.ecology.service.PluginServiceImpl" scope="page" />
<%
FileUpload fu = new FileUpload(request); // 通过FileUpload类生成了一个request请求的对象,可以调用request用户请求的参数
String type = Util.null2String(fu.getParameter("type"));
fu.getParameter("type") 是通过getParameter方法从FileUpload对象中获取名为"type"的参数的值 Util.null2String() 是泛微系统中的一个工具类方法,用于将参数的值转换为字符串类型。如果参数的值为null,则返回空字符串("")
Map result = new HashMap();
if("checkPluginFile".equalsIgnoreCase(type)) {
String fileHash = fu.getParameter("fileHash");
result = ps.checkPluginFile(fileHash);
} else if("mobileSetting".equalsIgnoreCase(type)) {
String settings = fu.getParameter("settings");
String timestamp = fu.getParameter("timestamp");
result = ps.syncMobileSetting(settings, timestamp);
// 这段代码是一个名为syncMobileSetting的方法。它接受两个参数var1和var2,它们分别用于传递settings和timestamp。 public Map<String, Object> syncMobileSetting(String var1, String var2) { // var1=settings , var2=timestamp HashMap var3 = new HashMap(); // 创建一个HashMap对象var3来存储最终返回的结果 RecordSet var4 = new RecordSet(); //创建一个RecordSet对象var4 try { // settings 的 值 必须遵循 [{"name":"小明","age":18},{"name":"小红","age":20}] 这种格式 // 本次传递,需要6个参数 // [{"scope":"","module":"","setting":"","modulename":"","include":"","orasc":""},] JSONArray var5 = JSONArray.fromObject(var1); // 使用JSONArray的静态方法fromObject将var1转换为JSONArray对象var5 List var6 = JSONArray.toList(var5, new HashMap(), new JsonConfig()); // 使用JSONArray的toList方法将var5转换为List对象var6, 并使用new JsonConfig()作为参数来处理转换过程中可能的特殊类型 // 执行两个SQL语句,首先是删除MobileSetting表中的所有记录,然后是插入一条新的记录到MobileSetting表中,这条记录的scope、module、fields和value根据传入的参数进行赋值 var4.executeSql("delete from MobileSetting"); var4.executeSql("insert into MobileSetting(scope,module,fields,value) values(0,0,'timestamp','" + var2 + "')"); // scope=0、module=0、fields='timestamp'、value=timestamp // 检查var6是否不为null且不为空。如果满足条件,进入循环遍历var6中的每个元素 if (var6 != null && !var6.isEmpty()) { ArrayList var7 = new ArrayList(); // 新列表 Iterator var8 = var6.iterator(); // 迭代器 [obj,obj] label76: while(true) { while(true) { while(var8.hasNext()) { Map var9 = (Map)var8.next(); // 在循环中,获取每个元素的scope和module,并根据不同的条件进行处理。根据条件的不同,可能执行不同的逻辑,例如将值添加到var7集合中或调用saveMobileDocSetting方法 int var10 = NumberUtils.toInt((String)var9.get("scope")); int var11 = NumberUtils.toInt((String)var9.get("module")); String var12 = (String)var9.get("setting"); String var13 = (String)var9.get("modulename"); String var14 = (String)var9.get("include"); String var15 = (String)var9.get("orasc"); if (var11 != 1 && var11 != 7 && var11 != 8 && var11 != 9 && var11 != 10) { // module 不等于 1,7,8,9,10 if (var11 != 2 && var11 != 3) { // module 不等于 2,3 if (StringUtils.isBlank(var12)) { // 判断var12是否为空,为空则赋值"0" var12 = "0"; } var7.add("" + var10 + Util.getSeparator() + var11 + Util.getSeparator() + "setting" + Util.getSeparator() + var12); // Util.getSeparator() 操作系统路径分隔符-> "/" // [scope]/[module]/setting/[setting] } else { // module // 传递了scope,setting,modulename 三个参数 ======================================================== this.saveMobileDocSetting(var10, var12, var13); // 转移到下面的 saveMobileDocSetting 方法处观察 ======================================================== } } else { if (StringUtils.isNotBlank(var12)) { String[] var16 = StringUtils.splitByWholeSeparator(var12, ","); if (var16 != null && var16.length > 0) { for(int var17 = 0; var17 < var16.length; ++var17) { var7.add("" + var10 + Util.getSeparator() + var11 + Util.getSeparator() + "workflowid" + Util.getSeparator() + var16[var17]); } } } else { var7.add("" + var10 + Util.getSeparator() + var11 + Util.getSeparator() + "workflowid" + Util.getSeparator() + "0"); } if (StringUtils.isNotBlank(var14)) { var7.add("" + var10 + Util.getSeparator() + var11 + Util.getSeparator() + "include" + Util.getSeparator() + var14); } if (StringUtils.isNotBlank(var15)) { var7.add("" + var10 + Util.getSeparator() + var11 + Util.getSeparator() + "orasc" + Util.getSeparator() + var15); } } } BatchRecordSet var19 = new BatchRecordSet(); // 循环遍历,最后将var7中的元素使用批量方式插入到数据库中 var19.executeSqlBatch("insert into MobileSetting(scope,module,fields,value) values(?,?,?,?)", var7); break label76; } } } var3.put("success", "1"); } catch (Exception var18) { // 在catch块中捕获异常,并将错误信息存储到var3中 logger.error("", var18); var3.put("error", "system error"); } return var3; }
saveMobileDocSetting
// 这段代码的作用是根据传入的参数,保存一些MobileDocSetting和MobileDocColSetting表中的记录。 private void saveMobileDocSetting(int var1, String var2, String var3) throws Exception { // scope,setting,modulename // 这个方法接收三个参数:var1是整型,var2和var3是字符串类型。方法通过判断var1是否大于0且var3不为空来执行接下来的操作。 if (var1 > 0 && !StringUtils.isBlank(var3)) { RecordSet var4 = new RecordSet(); BatchRecordSet var5 = new BatchRecordSet(); // BatchRecordSet和RecordSet都是用来处理数据库记录集的类。 // RecordSet是一个用于表示结果集的类,它包含了从数据库中查询得到的数据。RecordSet提供了一系列方法来操作和处理这些数据,例如获取字段值、移动指针位置、检查是否还有下一条记录等。 // BatchRecordSet是RecordSet的扩展,它可以实现批量执行SQL语句。它提供了一个方法executeSqlBatch,可以同时执行多条SQL语句,将多个SQL语句的结果集打包在一个BatchRecordSet中。 // 这两个类通常用于与数据库进行交互,执行SQL语句并处理查询结果。它们提供了简便的方法来获取和操作数据库中的数据 if (StringUtils.isBlank(var2)) { // 检查var2是否为空。如果是空的,它会执行一系列SQL语句来删除MobileDocColSetting和MobileDocSetting表中scope等于var1的记录,然后向MobileDocSetting表中插入一条新的记录,值为var1、var3、0、1、0 var4.executeSql("delete from MobileDocColSetting where columnid in (select columnid from MobileDocSetting where scope=" + var1 + ")"); var4.executeSql("delete from MobileDocSetting where scope=" + var1 + ""); var4.executeSql("insert into MobileDocSetting(scope,name,source,showOrder,isreplay) values(" + var1 + ",'" + var3 + "',0,1,0)"); } else { // 对setting进行操作 String var6 = ""; var2 = StringUtils.trimToEmpty(var2); // 去除两端空白字符 // 去掉 @(有的话) // @xx|1#xxxx 示例 boolean var7 = var2.startsWith("@"); // 判断头部@,赋值逻辑 true|false if (var7) { var2 = var2.substring(1); // 提取 } // [ "xx|1" , "xxxx" ] 示例分割 String[] var8 = StringUtils.split(var2, '#'); // 分割为字符数组 if (var8 == null || var8.length == 0) { return; // 数组为空则返回 } for(int var9 = 0; var9 < var8.length; ++var9) { // 循环 var8 数组 String var10 = var8[var9]; String[] var11 = var10.split("\\|"); // 以 | 字符进行分割 // var11 = ["xx","1"] 示例再分割 if (var11 != null && var11.length != 0) { // 如果数组不为空,则创建一些变量 String var12 = ""; boolean var13 = false; boolean var14 = false; String[] var15 = null; boolean var16 = false; int var21; int var22; int var23; if (var7) { // 如果var2头部存在@ var12 = URLDecoder.decode(var11[0], "UTF-8"); // var11[0] URL解码 "xx" var21 = NumberUtils.toInt(var11[1]); // var11[1] 强制转换为数字 // "1" -> 1 var22 = var11.length > 2 ? NumberUtils.toInt(var11[2]) : 1; // 长度大于2,转换var11[2]赋值,否则赋值1 var15 = var11.length > 3 ? StringUtils.split(var11[3], ',') : null; // 长度大于3,以逗号分割var11[3]赋值,否则赋值null var23 = var11.length > 4 ? ("1".equals(var11[4]) ? 1 : 0) : 0; // 长度大于4,判断var11[4]是否等于"1",是则赋值1,否则赋值0,长度小于4,赋值0 } else { // 头部不存在@ var12 = var3; // var12直接等于modulename,因为没有进行解码操作,所以无法利用 var21 = 1; var22 = var11.length > 0 ? NumberUtils.toInt(var11[0]) : 1; // 长度大于0,转换var11[0]赋值,否则赋值1 var15 = var11.length > 1 ? StringUtils.split(var11[1], ',') : null; // 长度大于1,以逗号分割var11[1]赋值,否则赋值null var23 = var11.length > 2 ? ("1".equals(var11[2]) ? 1 : 0) : 0; // 长度大于2,判断var11[2]是否等于"1",是则赋值1,否则赋值0,长度小于4,赋值0 } int var17 = 0; // 代码首先执行一条SQL语句,查询MobileDocSetting表中name等于var12且scope等于var1(scope)的记录的columnid ================================================================ |// var12 = "xx" // var11[0]=xx可控 |// var1 = scope |// 存在注入的点位 |String var18 = "select columnid from MobileDocSetting where name = '" + var12 + "' and scope =" + var1; | ================================================================ var4.executeSql(var18); if (var4.next()) { var17 = var4.getInt("columnid"); // 获取columnid值 // 如果查询结果不为空,就更新该记录的其他字段值(scope、name、source、showOrder、isreplay) var4.executeSql("update MobileDocSetting set scope = " + var1 + ",name = '" + var12 + "',source=" + var22 + ",showOrder = " + var21 + ",isreplay = " + var23 + " where columnid=" + var17); // 删除MobileDocColSetting表中columnid等于查询结果的记录 var4.executeSql("delete from MobileDocColSetting where columnid=" + var17); } else { // 查询结果为空,代码会向MobileDocSetting表中插入一条新的记录 var4.executeSql("insert into MobileDocSetting(scope,name,source,showOrder,isreplay) values(" + var1 + ",'" + var12 + "'," + var22 + "," + var21 + "," + var23 + ")"); var4.executeSql("select max(columnid) as maxid from MobileDocSetting"); //查询出插入记录后的最大columnid值) if (var4.next()) { var17 = var4.getInt("maxid"); } } if (var15 != null && var15.length != 0 && var17 > 0) { // var15不等于空并且var17(最大columnid值)大于0 var6 = var6 + var17 + ","; // [id],[id] ArrayList var19 = new ArrayList(); for(int var20 = 0; var20 < var15.length; ++var20) { // 遍历var15 var19.add("" + var17 + Util.getSeparator() + var15[var20]); // [ id|var,id|var ] } if (var19 != null && !var19.isEmpty()) { // 如果不为空,则插入值 var5.executeSqlBatch("insert into MobileDocColSetting(columnid,docid) values(?,?)", var19); } } } } // 去掉尾巴上的逗号 if (var6.endsWith(",")) { var6 = var6.substring(0, var6.length() - 1); } if (!var6.equals("")) { // var6不为空 // 删除 columnid 不在 va6中的数据行 var4.executeSql("delete from MobileDocColSetting where columnid in (select columnid from MobileDocSetting where scope=" + var1 + ") and columnid not in(" + var6 + ")"); var4.executeSql("delete from MobileDocSetting where scope=" + var1 + " and columnid not in(" + var6 + ")"); } } } }
} else if("serverSetting".equalsIgnoreCase(type)){
RongConfig rc = RongService.getRongConfig();
result.put("rongAppUDID", rc.getAppUDID());
result.put("rongAppKey", rc.getAppKey());
result.put("rongAppSecret", rc.getAppSecret());
result.put("rongAppUDIDNew", rc.getAppUDIDNew());
result.put("openfireModule", rc.getOpenfire());
result.put("openfireServerSecrect", rc.getServerSecrect());
result.put("openfireDomain", rc.getOpenfireDomain());
result.put("openfireEMobileUrl", rc.getOpenfireEMobileUrl());
result.put("openfireMobileClientUrl", rc.getOpenfireMobileClientUrl());
} else if("pushSetting".equalsIgnoreCase(type)){
String serverUrl = Prop.getPropValue("EMobile4", "serverUrl");
String EMobilePush = Prop.getPropValue("EMobile4", "EMobilePush");
String pushKey= Prop.getPropValue("EMobile4", "pushKey");
result.put("serverUrl", serverUrl );
result.put("EMobilePush", EMobilePush );
result.put("pushKey", pushKey );
} else if("changePushKey".equalsIgnoreCase(type)){
String pushKey= fu.getParameter("pushKey");
Prop.setPropValue("EMobile4", "pushKey",pushKey);
} else if("changeServerUrl".equalsIgnoreCase(type)){
String serverUrl= fu.getParameter("serverUrl");
Prop.setPropValue("EMobile4", "serverUrl",serverUrl);
} else if("openEMobilePush".equalsIgnoreCase(type)){
String EMobilePush= fu.getParameter("EMobilePush");
Prop.setPropValue("EMobile4", "EMobilePush",EMobilePush);
} else{
result = ps.checkServerStatus();
}
if(result!=null) {
JSONObject jo = JSONObject.fromObject(result);
//System.out.println(jo);
out.println(jo.toString());
}
%>
代码翻译
- 通过指令声明页面采用Java语言编写,并设置字符编码等属性。
- 导入所需的Java包和类。
- 创建一个PluginServiceImpl对象,用于后续调用相关方法。
- 设置响应的Content-Type为JSON类型。
- 通过FileUpload类处理请求,获取参数的值。
- 根据type参数的不同,执行不同的逻辑操作。
- 当type为"checkPluginFile"时,获取fileHash参数并调用PluginServiceImpl的checkPluginFile方法处理。
- 当type为"mobileSetting"时,获取settings参数,进行MobileSettingsValidator的验证,如果验证通过则调用PluginServiceImpl的syncMobileSetting方法进行同步设置。
- 当type为"serverSetting"时,获取RongConfig对象的配置信息并放入Map中。
- 当type为"mobileEncode"时,通过反射调用PluginServiceImpl的mobileEncode方法进行移动端编码。
- 其他情况下,默认将status设置为true、syncSetting设置为0,放入result中。
- 将结果以JSON格式输出。
- 这段代码主要是实现了泛微协同办公CheckServer.jsp的功能,根据不同的type参数,处理不同的逻辑,并将结果以JSON格式输出。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)