freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

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

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

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

FreeBuf+小程序

FreeBuf+小程序

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

因为漏洞未公开,就不直接放出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