浅析Smartbi逻辑漏洞-2

admin 2024年10月29日00:19:41评论10 views字数 5954阅读19分50秒阅读模式

浅析Smartbi逻辑漏洞(2)

写在前面

仅分享逻辑漏洞部分补丁绕过思路,不提供完整payload

厂商已发布补丁:https://www.smartbi.com.cn/patchinfo

正文

简单提一下,补丁部分由smartbi.security.patch.PatchFilter(来源于SecurityPatchExt.ext)做加载并处理,这里我们主要关注补丁返回的状态码的具体含义即可,可以看到只有返回0的时候,filter链才能继续通过doFilter继续传递

123456789101112131415161718
Iterator var10 = rules.iterator();while(var10.hasNext()) {    URLPatchRule rule = (URLPatchRule)var10.next();    int result = rule.patch(uri, req, resp, chain);    switch (result) {        case 0:        default:            break;        case 1:            resp.sendError(403);            return;        case 2:            return;    }}chain.doFilter(request, response);

首先我们看看老板本对这点的patch的部分,如果queryString以windowUnloading开头,那么会做两件事,首先通过request.getParameter获取ClassName与MethodName的值,接着对windowUnloading中的值做解码并赋值给urlClassName与urlMethodName参数,最后补丁中会对request.getParameter与解码的值做对比,如果其中classname与methodname值不相等,那么就会返回1,也就是做拦截

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
public int patch(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {        return this.assertQueryString(request);    }private int assertQueryString(HttpServletRequest request) {    String query = request.getQueryString();    if (StringUtil.isNullOrEmpty(query)) {        return 0;    } else if (!query.startsWith("windowUnloading")) {        return 0;    } else if (!query.startsWith("windowUnloading=&") && !query.startsWith("windowUnloading&")) {        return 1;    } else {        String paramClassName = request.getParameter("className");        String paramMethodName = request.getParameter("methodName");        if (!StringUtil.isNullOrEmpty(paramClassName) && !StringUtil.isNullOrEmpty(paramMethodName)) {            try {                String content = "";                String windowUnloadingStr = query.length() > "windowUnloading".length() && query.charAt("windowUnloading".length()) == '=' ? "windowUnloading=&" : "windowUnloading&";                if (query.length() > windowUnloadingStr.length()) {                    content = query.substring(windowUnloadingStr.length());                    if (content.endsWith("=")) {                        content = content.substring(0, content.length() - 1);                    }                    content = URLDecoder.decode(content, "UTF-8");                }                String urlClassName = "";                String urlMethodName = "";                if (content.indexOf("className=") == -1 && content.indexOf("methodName=") == -1) {                    String[] decode = RMICoder.decode(content);                    urlClassName = decode[0];                    urlMethodName = decode[1];                } else {                    Map<String, String> map = HttpUtil.parseQueryString(content);                    urlClassName = (String)map.get("className");                    urlMethodName = (String)map.get("methodName");                }                if (StringUtil.isNullOrEmpty(urlClassName) && StringUtil.isNullOrEmpty(urlMethodName)) {                    return 0;                } else {                    return paramClassName.equals(urlClassName) && paramMethodName.equals(urlMethodName) ? 0 : 1;                }            } catch (Exception var10) {                return 0;            }        } else {            return 0;        }    }

那么我们可以思考这样的方式真的有效么?答案当时是否

在上篇文章中我们就提到,在CheckIsLoggedFilter中如果不考虑解码操作,对于classname与methodname的获取,有三种方式GET\POST\Multipart(这里POST指标准形式通过Body传参),补丁的操作是比对request.getParameteter与windowUnloading解码的值是否一致,我曾经也在其他文章当中写到过,在tomcat环境下,request.getParameteter只能获取到GET与POST,对于Multipart默认情况下是不做支持的(需要单独配置才能开启),当然这里也是没配置

我们也可以通过diff发现,如果content-type头以multipart开头就会返回状态1,也就是拦截

浅析Smartbi逻辑漏洞-2

在上面的基础上我们很容易就可以构造出这样形式的请求包,像之前那样通过windowUnloading绕过方法校验限制

123456789101112131415161718
POST /smartbi/vision/RMIServlet?windowUnloading=xxxx HTTP/1.1Host: xxxxContent-Type: multipart/form-data;charset=UTF-8;boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwAConnection: close------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="className"xxxService------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="methodName"xxx------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="params"['xxx']------WebKitFormBoundaryrGKCBY7qhFd3TrwA

我们将真正要掉用的方法放在multipart中,由于此时request.getParameter取值为null,StringUtil.isNullOrEmpty为true,自然也就绕过了补丁的限制

12345678
if (!query.startsWith("windowUnloading")) {            return 0;        } else if (!query.startsWith("windowUnloading=&") && !query.startsWith("windowUnloading&")) {            return 1;        } else {            String paramClassName = request.getParameter("className");            String paramMethodName = request.getParameter("methodName");            if (!StringUtil.isNullOrEmpty(paramClassName) && !StringUtil.isNullOrEmpty(paramMethodName)) {

最终在RMIServlet解析参数时,又单独对multipart做了处理,这时我们真正要掉用的恶意类与方法又成功恢复出来,完成了老补丁的绕过

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
public static RMIInfo parseRMIInfo(HttpServletRequest request, boolean forceParse) {        if (!"/vision/RMIServlet".equals(request.getServletPath()) && !forceParse) {            return null;        } else {            RMIInfo info = getRMIInfoFromRequest(request);            if (info != null) {                return info;            } else {                String className = request.getParameter("className");                String methodName = request.getParameter("methodName");                String params = request.getParameter("params");                if (StringUtil.isNullOrEmpty(className) && StringUtil.isNullOrEmpty(methodName) && StringUtil.isNullOrEmpty(params) && request.getContentType() != null && request.getContentType().startsWith("multipart/form-data;")) {                    DiskFileItemFactory dfif = new DiskFileItemFactory();                    ServletFileUpload upload = new ServletFileUpload(dfif);                    String encodeString = null;                    try {                        List<FileItem> fileItems = upload.parseRequest(request);                        request.setAttribute("UPLOAD_FILE_ITEMS", fileItems);                        Iterator var10 = fileItems.iterator();                        while(var10.hasNext()) {                            FileItem fileItem = (FileItem)var10.next();                            if (fileItem.isFormField()) {                                String itemName = fileItem.getFieldName();                                String itemValue = fileItem.getString("UTF-8");                                if ("className".equals(itemName)) {                                    className = itemValue;                                } else if ("methodName".equals(itemName)) {                                    methodName = itemValue;                                } else if ("params".equals(itemName)) {                                    params = itemValue;                                } else if ("encode".equals(itemName)) {                                    encodeString = itemValue;                                }                            }                        }                    } catch (UnsupportedEncodingException | FileUploadException var14) {                        LOG.error(var14.getMessage(), var14);                    }                    if (!StringUtil.isNullOrEmpty(encodeString)) {                        String[] decode = (String[])((String[])CodeEntry.decode(encodeString, true));                        className = decode[0];                        methodName = decode[1];                        params = decode[2];                    }                }                if (className == null && methodName == null) {                    className = (String)request.getAttribute("className");                    methodName = (String)request.getAttribute("methodName");                    params = (String)request.getAttribute("params");                }                info = new RMIInfo();                info.setClassName(className);                info.setMethodName(methodName);                info.setParams(params);                request.setAttribute("ATTR_KEY_RMIINFO", info);                return info;            }        }    }

当然还有些东西只能说,懂得都懂

- source:y4tacker

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

发表评论

匿名网友 填写信息