freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

泛微9.0sql注入问题代码分析
x318846679 2023-07-08 02:31:28 190944
所属地 江苏省

因为漏洞未公开,就不直接放出payload了,已标注存在问题的点位,请自行分析和构造payload,这个漏洞理解还是不难的

1688754453_64a859154d9ea7ef32cbd.png!small

<%@ 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());

}

%>

代码翻译

  1. 通过指令声明页面采用Java语言编写,并设置字符编码等属性。
  2. 导入所需的Java包和类。
  3. 创建一个PluginServiceImpl对象,用于后续调用相关方法。
  4. 设置响应的Content-Type为JSON类型。
  5. 通过FileUpload类处理请求,获取参数的值。
  6. 根据type参数的不同,执行不同的逻辑操作。
  7. 当type为"checkPluginFile"时,获取fileHash参数并调用PluginServiceImpl的checkPluginFile方法处理。
  8. 当type为"mobileSetting"时,获取settings参数,进行MobileSettingsValidator的验证,如果验证通过则调用PluginServiceImpl的syncMobileSetting方法进行同步设置。
  9. 当type为"serverSetting"时,获取RongConfig对象的配置信息并放入Map中。
  10. 当type为"mobileEncode"时,通过反射调用PluginServiceImpl的mobileEncode方法进行移动端编码。
  11. 其他情况下,默认将status设置为true、syncSetting设置为0,放入result中。
  12. 将结果以JSON格式输出。
  13. 这段代码主要是实现了泛微协同办公CheckServer.jsp的功能,根据不同的type参数,处理不同的逻辑,并将结果以JSON格式输出。
# 漏洞 # 网络安全 # web安全 # 漏洞分析
本文为 x318846679 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
x318846679 LV.2
这家伙太懒了,还未填写个人描述!
  • 2 文章数
  • 0 关注者
CTF-安卓反编译-入门
2023-08-22