Apache XML-RPC是一个java的XML-RPC库。XML-RPC是在XML的帮助下通过HTTP进行远程过程调用的协议。 Apache XML-RPC可以在客户端用于进XML-RPC调用,也可以在服务器端用XML-RPC公开一些函数。现在ws-xmlrpc库不被Apache支持。 最新版本是2013年发布的3.1.3版本。但是,许多应用程序仍然使用ws-xmlrpc库
新建Maven工程,引入如下依赖
<dependencies> <dependency> <groupId>org.apache.xmlrpc</groupId> <artifactId>xmlrpc-common</artifactId> <version>3.1.3</version> </dependency> <dependency> <groupId>org.apache.xmlrpc</groupId> <artifactId>xmlrpc-server</artifactId> <version>3.1.3</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> </dependencies>
主方法 App.java
public class App { private static final int port = 8888; public static void main(String[] args) throws Exception { WebServer webServer = new WebServer(port); XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer(); PropertyHandlerMapping phm = new PropertyHandlerMapping(); phm.addHandler("User", User.class); xmlRpcServer.setHandlerMapping(phm); XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) xmlRpcServer.getConfig(); serverConfig.setEnabledForExtensions(true); webServer.start(); } }
接收传入对象 User.java
public class User { public String welcome(String name) { return "welcome " + name; } }
运行 App.java
后即在 8888
端口建立 HTTP
服务。即可使用Burpsuite进行发包测试 参考 官网帮助文档 ,可以发现xmlrpc支持 java.io.Serializable
序列化后的base64编码的数据,但是需要引用命名空间 http://ws.apache.org/xmlrpc/namespaces/extensions
。所以使用 ysoserial
的 CommonsCollections5
生成payload。 因为本机测试环境为jdk1.8,用其它payload会爆错 。 然后使用certutil -encode 将生成的文件进行base64编码。
当客户端发送请求时, org.apache.xmlrpc.server.XmlRpcStreamServer.getRequest(XmlRpcStreamRequestConfig, InputStream)
方法会接收到该请求,该方法会将请求内容封闭到XMLReader对象中。
protected XmlRpcRequest getRequest(final XmlRpcStreamRequestConfig pConfig, InputStream pStream) throws XmlRpcException { final XmlRpcRequestParser parser = new XmlRpcRequestParser(pConfig, getTypeFactory()); final XMLReader xr = SAXParsers.newXMLReader(); xr.setContentHandler(parser); try { xr.parse(new InputSource(pStream)); } catch (SAXException e) { Exception ex = e.getException(); if (ex != null && ex instanceof XmlRpcException) { throw (XmlRpcException) ex; } throw new XmlRpcException("Failed to parse XML-RPC request: " + e.getMessage(), e); } catch (IOException e) { throw new XmlRpcException("Failed to read XML-RPC request: " + e.getMessage(), e); } final List params = parser.getParams(); return new XmlRpcRequest(){ public XmlRpcRequestConfig getConfig() { return pConfig; } public String getMethodName() { return parser.getMethodName(); } public int getParameterCount() { return params == null ? 0 : params.size(); } public Object getParameter(int pIndex) { return params.get(pIndex); } }; }
然后程序会走到 org.apache.xmlrpc.parser.XmlRpcRequestParser.endElement(String, String, String)
方法来获取各个元素的值
public void endElement(String pURI, String pLocalName, String pQName) throws SAXException { switch(--level) { case 0: break; case 1: if (inMethodName) { if ("".equals(pURI) && "methodName".equals(pLocalName)) { if (methodName == null) { methodName = ""; } } else { throw new SAXParseException("Expected /methodName, got " + new QName(pURI, pLocalName), getDocumentLocator()); } inMethodName = false; } else if (!"".equals(pURI) || !"params".equals(pLocalName)) { throw new SAXParseException("Expected /params, got " + new QName(pURI, pLocalName), getDocumentLocator()); } break; case 2: if (!"".equals(pURI) || !"param".equals(pLocalName)) { throw new SAXParseException("Expected /param, got " + new QName(pURI, pLocalName), getDocumentLocator()); } break; case 3: if (!"".equals(pURI) || !"value".equals(pLocalName)) { throw new SAXParseException("Expected /value, got " + new QName(pURI, pLocalName), getDocumentLocator()); } endValueTag(); break; default: super.endElement(pURI, pLocalName, pQName); break; } }
当获取到最后的元素时,会执行 org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endValueTag()
方法
protected void endValueTag() throws SAXException { if (inValueTag) { if (typeParser == null) { addResult(text.toString()); text.setLength(0); } else { typeParser.endDocument(); try { addResult(typeParser.getResult()); } catch (XmlRpcException e) { throw new SAXException(e); } typeParser = null; } } else { throw new SAXParseException("Invalid state: Not inside value tag.", getDocumentLocator()); } }
即会执行typeParser.getResult(),并且此时的typeParser为SerializableParser,
public class SerializableParser extends ByteArrayParser { public Object getResult() throws XmlRpcException { try { byte[] res = (byte[]) super.getResult(); ByteArrayInputStream bais = new ByteArrayInputStream(res); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } catch (IOException e) { throw new XmlRpcException("Failed to read result object: " + e.getMessage(), e); } catch (ClassNotFoundException e) { throw new XmlRpcException("Failed to load class for result object: " + e.getMessage(), e); } } }
从而造成了反序列化命令执行漏洞。